Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> * The stack of basic blocks and scopes the current traversal is in. */ private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * Javascript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate<Var> varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.<Var>alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate<Var> varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } /** * Gets the reference collection for the given variable. */ public ReferenceCollection getReferenceCollection(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { Var v = t.getScope().getVar(n.getString()); if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, parent, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek();

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> blockStack.push(new BasicBlock(parent, n)); } /** * Updates block statck and invokes any additional behavior. */ public void exitScope(NodeTraversal t) { blockStack.pop(); behavior.afterExitScope(t, referenceMap); } /** * Updates block stack. */ public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explcit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.getType() == Token.CASE; } private void addReference(NodeTraversal t, Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference, t, v); } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExit

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Scope(NodeTraversal t, Map<Var, ReferenceCollection> referenceMap); } static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, Map<Var, ReferenceCollection> referenceMap) {} }; /** * A collection of references. Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection { List<Reference> references = Lists.newArrayList(); void add(Reference reference, NodeTraversal t, Var v) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a definitive judgement. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore( references.get(i).getBasicBlock())) { return false; } } return true; } /** * Whether the variable is escaped into an inner scope. */ boolean isEscaped() { Scope scope = null; for (Reference ref : references) { if (scope == null) { scope = ref.scope; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> true; } boolean firstReferenceIsAssigningDeclaration() { int size = references.size(); if (size > 0 && references.get(0).isInitializingDeclaration()) { return true; } return false; } } /** * Represents a single declaration or reference to a variable. */ static final class Reference { private static final Set<Integer> DECLARATION_PARENTS = ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); private final Node nameNode; private final Node parent; private final Node grandparent; private final BasicBlock basicBlock; private final Scope scope; private final String sourceName; Reference(Node nameNode, Node parent, NodeTraversal t, BasicBlock basicBlock) { this(nameNode, parent, parent.getParent(), basicBlock, t.getScope(), t.getSourceName()); } // Bleeding functions are weird, because the declaration does // not appear inside their scope. So they need their own constructor. static Reference newBleedingFunction(NodeTraversal t, BasicBlock basicBlock, Node func) { return new Reference(func.getFirstChild(), func, func.getParent(), basicBlock, t.getScope(), t.getSourceName()); } private Reference(Node nameNode, Node parent, Node grandparent, BasicBlock basicBlock, Scope scope, String sourceName) { this.nameNode = nameNode; this.parent = parent; this.grandparent = grandparent; this.basicBlock = basicBlock; this.scope = scope; this.sourceName = sourceName; } boolean isDeclaration() { return DECLARATION_PARENTS.contains(parent.getType()) || parent.getType() == Token.LP && grandparent.getType() == Token.FUNCTION; } boolean isVarDeclaration() { return parent.getType() == Token.VAR; } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(parent); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> isDeclaration() && (parent.getType() != Token.VAR || nameNode.getFirstChild() != null); } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { return (parent.getType() == Token.FUNCTION) ? parent : NodeUtil.getAssignedValue(getNameNode()); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return parent; } Node getNameNode() { return nameNode; } Node getGrandparent() { return grandparent; } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.getType() == Token.VAR) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { return parent.getType() == Token.ASSIGN && parent.getFirstChild() == nameNode; } boolean isLvalue() { int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } for (currentBlock = thatBlock; currentBlock != null && currentBlock != this; currentBlock = currentBlock.getParent()) { if (currentBlock.isHoisted) { return false; } } return currentBlock == this; } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> = refInfo; } } /** * Builds up information about nodes in each scope. When exiting the * scope, inspects all variables in that scope, and inlines any * that we can. */ private class InliningBehavior implements Behavior { /** * A list of variables that should not be inlined, because their * reference information is out of sync with the state of the AST. */ private final Set<Var> staleVars = Sets.newHashSet(); /** * Stored possible aliases of variables that never change, with * all the reference info about those variables. Hashed by the NAME * node of the variable being aliased. */ final Map<Node, AliasCandidate> aliasCandidates = Maps.newHashMap(); @Override public void afterExitScope(NodeTraversal t, Map<Var, ReferenceCollection> referenceMap) { collectAliasCandidates(t, referenceMap); doInlinesForScope(t, referenceMap); } /** * If any of the variables are well-defined and alias other variables, * mark them as aliasing candidates. */ private void collectAliasCandidates(NodeTraversal t, Map<Var, ReferenceCollection> referenceMap) { if (mode != Mode.CONSTANTS_ONLY) { for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); ReferenceCollection referenceInfo = referenceMap.get(v); // NOTE(nicksantos): Don't handle variables that are never used. // The tests are much easier to write if you don't, and there's // another pass that handles unused variables much more elegantly. if (referenceInfo != null && referenceInfo.references.size() >= 2 && referenceInfo.isWellDefined() && referenceInfo.isAssignedOnceInLifetime()) { Reference init = referenceInfo.getInitializingReference(); Node value = init.getAssignedValue(); if (value != null && value.getType() == Token.NAME) { aliasCandidates.put(value, new AliasCandidate(v, referenceInfo)); } } } } } /** * For all variables in this scope, see if they are only used once. * If it looks safe to do so, inline them.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> // The variable likely only read once, try some more // complex inlining heuristics. Reference reference = referenceInfo.references.get( firstRefAfterInit - 1); if (canInline(declaration, init, reference)) { inline(v, declaration, init, reference); staleVars.add(v); } } else if (declaration != init && refCount == 2) { if (isValidDeclaration(declaration) && isValidInitialization(init)) { // The only reference is the initialization, remove the assignment and // the variable declaration. Node value = init.getAssignedValue(); Preconditions.checkNotNull(value); inlineWellDefinedVariable(v, value, referenceInfo.references); staleVars.add(v); } } // If this variable was not inlined normally, check if we can // inline an alias of it. (If the variable was inlined, then the // reference data is out of sync. We're better off just waiting for // the next pass.) if (!staleVars.contains(v) && referenceInfo.isWellDefined() && referenceInfo.isAssignedOnceInLifetime()) { List<Reference> refs = referenceInfo.references; for (int i = 1 /* start from a read */; i < refs.size(); i++) { Node nameNode = refs.get(i).getNameNode(); if (aliasCandidates.containsKey(nameNode)) { AliasCandidate candidate = aliasCandidates.get(nameNode); if (!staleVars.contains(candidate.alias)) { Reference aliasInit; aliasInit = candidate.refInfo.getInitializingReference(); Node value = aliasInit.getAssignedValue(); Preconditions.checkNotNull(value); inlineWellDefinedVariable(candidate.alias, value, candidate.refInfo.references); staleVars.add(candidate.alias); } } } } } /** * If there are any variable references in the given node tree, blacklist * them to prevent the pass from trying to inline the variable. */ private void blacklistVarReferencesInTree(Node root, Scope scope) { for (Node c = root.getFirstChild(); c != null; c = c.getNext()) { blacklistVarReferencesInTree(c, scope); } if (root.getType

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>() == Token.NAME) { staleVars.add(scope.getVar(root.getString())); } } /** * Whether the given variable is forbidden from being inlined. */ private boolean isVarInlineForbidden(Var var) { // A variable may not be inlined if: // 1) The variable is exported, // 2) A reference to the variable has been inlined. We're downstream // of the mechanism that creates variable references, so we don't // have a good way to update the reference. Just punt on it. // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME return compiler.getCodingConvention().isExported(var.name) || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name) || staleVars.contains(var); } /** * Do the actual work of inlining a single declaration into a single * reference. */ private void inline(Var v, Reference declaration, Reference init, Reference reference) { Node value = init.getAssignedValue(); Preconditions.checkState(value != null); // Check for function declarations before the value is moved in the AST. boolean isFunctionDeclaration = NodeUtil.isFunctionDeclaration(value); inlineValue(v, reference, value.detachFromParent()); if (declaration != init) { Node expressRoot = init.getGrandparent(); Preconditions.checkState(expressRoot.getType() == Token.EXPR_RESULT); NodeUtil.removeChild(expressRoot.getParent(), expressRoot); } // Function declarations have already been removed. if (!isFunctionDeclaration) { removeDeclaration(declaration); } else { compiler.reportCodeChange(); } } /** * Inline an immutable variable into all of its references. */ private void inlineWellDefinedVariable(Var v, Node value, List<Reference> refSet) { Reference decl = refSet.get(0); for (int i = 1; i < refSet.size(); i++) { inlineValue(v, refSet.get(i), value.cloneTree()); } removeDeclaration(decl); } /** * Inline a declared constant. */ private void inlineDeclaredConstant(Var v, Node value, List

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS><Reference> refSet) { // Replace the references with the constant value Reference decl = null; for (Reference r : refSet) { if (r.getNameNode() == v.getNameNode()) { decl = r; } else { inlineValue(v, r, value.cloneTree()); } } removeDeclaration(decl); } /** * Remove the given VAR declaration. */ private void removeDeclaration(Reference declaration) { Node varNode = declaration.getParent(); varNode.removeChild(declaration.getNameNode()); // Remove var node if empty if (!varNode.hasChildren()) { Preconditions.checkState(varNode.getType() == Token.VAR); Node grandparent = declaration.getGrandparent(); NodeUtil.removeChild(grandparent, varNode); } compiler.reportCodeChange(); } /** * Replace the given reference with the given value node. * * @param v The variable that's referenced. * @param ref The reference to replace. * @param value The node tree to replace it with. This tree should be safe * to re-parent. */ private void inlineValue(Var v, Reference ref, Node value) { if (ref.isSimpleAssignmentToName()) { // This is the initial assignment. ref.getGrandparent().replaceChild(ref.getParent(), value); } else { ref.getParent().replaceChild(ref.getNameNode(), value); } blacklistVarReferencesInTree(value, v.scope); compiler.reportCodeChange(); } /** * Determines whether the given variable is declared as a constant * and may be inlined. */ private boolean isInlineableDeclaredConstant(Var var, ReferenceCollection refInfo) { if (!identifyConstants.apply(var)) { return false; } if (!refInfo.isAssignedOnceInLifetime()) { return false; } Reference init = refInfo.getInitializingReferenceForConstants(); if (init == null) { return false; } Node value = init.getAssignedValue(); if (value == null) { // This constant is either externally defined or initialized indirectly // (e.g. in an function expression used to hide // temporary variables), so the constant

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> is ineligible for inlining. return false; } // Is the constant's value immutable? if (!NodeUtil.isImmutableValue(value)) { return false; } // Determine if we should really inline a String or not. return value.getType() != Token.STRING || isStringWorthInlining(var, refInfo.references); } /** * Compute whether the given string is worth inlining. */ private boolean isStringWorthInlining(Var var, List<Reference> refs) { if (!inlineAllStrings && !var.isDefine()) { int len = var.getInitialValue().getString().length() + "''".length(); // if not inlined: var xx="value"; .. xx .. xx .. // The 4 bytes per reference is just a heuristic: // 2 bytes per var name plus maybe 2 bytes if we don't inline, e.g. // in the case of "foo " + CONST + " bar" int noInlineBytes = "var xx=;".length() + len + 4 * (refs.size() - 1); // if inlined: // I'm going to assume that half of the quotes will be eliminated // thanks to constant folding, therefore I subtract 1 (2/2=1) from // the string length. int inlineBytes = (len - 1) * (refs.size() - 1); // Not inlining if doing so uses more bytes, or this constant is being // defined. return noInlineBytes >= inlineBytes; } return true; } /** * @return true if the provided reference and declaration can be safely * inlined according to our criteria */ private boolean canInline( Reference declaration, Reference initialization, Reference reference) { if (!isValidDeclaration(declaration) || !isValidInitialization(initialization) || !isValidReference(reference)) { return false; } // If the value is read more than once, skip it. // VAR declarations and EXPR_RESULT don't need the value, but other // ASSIGN expressions parents do. if (declaration != initialization && initialization.getGrandparent().getType() != Token.EXPR_RESULT) { return false; } // Be very conserv

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>ative and do no cross control structures or // scope boundaries if (declaration.getBasicBlock() != initialization.getBasicBlock() || declaration.getBasicBlock() != reference.getBasicBlock()) { return false; } // Do not inline into a call node. This would change // the context in which it was being called. For example, // var a = b.c; // a(); // should not be inlined, because it calls a in the context of b // rather than the context of the window. // var a = b.c; // f(a) // is ok. Node value = initialization.getAssignedValue(); Preconditions.checkState(value != null); if (value.getType() == Token.GETPROP && reference.getParent().getType() == Token.CALL && reference.getParent().getFirstChild() == reference.getNameNode()) { return false; } // Bug 2388531: Don't inline subclass definitions into class defining // calls as this confused class removing logic. if (value.getType() == Token.FUNCTION) { Node callNode = reference.getParent(); if (reference.getParent().getType() == Token.CALL) { SubclassRelationship relationship = compiler.getCodingConvention().getClassesDefinedByCall(callNode); if (relationship != null) { return false; } } } return canMoveAggressively(value) || canMoveModerately(initialization, reference); } /** * If the value is a literal, we can cross more boundaries to inline it. */ private boolean canMoveAggressively(Node value) { // Function expressions and other mutable objects can move within // the same basic block. return NodeUtil.isLiteralValue(value) || value.getType() == Token.FUNCTION; } /** * If the value of a variable is not constant, then it may read or modify * state. Therefore it cannot be moved past anything else that may modify * the value being read or read values that are modified. */ private boolean canMoveModerately( Reference initialization, Reference reference) { // Check if declaration can be inlined without passing // any side-effect causing nodes. Iterator<Node> it; if

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> (initialization.getParent().getType() == Token.VAR) { it = NodeIterators.LocalVarMotion.forVar( initialization.getNameNode(), // NAME initialization.getParent(), // VAR initialization.getGrandparent()); // VAR container } else if (initialization.getParent().getType() == Token.ASSIGN) { Preconditions.checkState( initialization.getGrandparent().getType() == Token.EXPR_RESULT); it = NodeIterators.LocalVarMotion.forAssign( initialization.getNameNode(), // NAME initialization.getParent(), // ASSIGN initialization.getGrandparent(), // EXPR_RESULT initialization.getGrandparent().getParent()); // EXPR container } else { throw new IllegalStateException("Unexpected initialiation parent"); } Node targetName = reference.getNameNode(); while (it.hasNext()) { Node curNode = it.next(); if (curNode == targetName) { return true; } } return false; } /** * @return true if the reference is a normal VAR or FUNCTION declaration. */ private boolean isValidDeclaration(Reference declaration) { return (declaration.getParent().getType() == Token.VAR && declaration.getGrandparent().getType() != Token.FOR) || NodeUtil.isFunctionDeclaration(declaration.getParent()); } /** * @return Whether there is a initial value. */ private boolean isValidInitialization(Reference initialization) { if (initialization == null) { return false; } else if (initialization.isDeclaration()) { // The reference is a FUNCTION declaration or normal VAR declaration // with a value. return NodeUtil.isFunctionDeclaration(initialization.getParent()) || initialization.getNameNode().getFirstChild() != null; } else { Node parent = initialization.getParent(); Preconditions.checkState( parent.getType() == Token.ASSIGN && parent.getFirstChild() == initialization.getNameNode()); return true; } } /** * @return true if the reference is a candidate for inlining */ private boolean isValidReference(Reference reference) { return !reference.isDeclaration() && !reference.isLvalue(); } /** * Determines whether the reference collection describes a variable that * is initialized to an immutable value, never modified, and defined before * every reference.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> */ private boolean isImmutableAndWellDefinedVariable(Var v, ReferenceCollection refInfo) { List<Reference> refSet = refInfo.references; int startingReadRef = 1; Reference refDecl = refSet.get(0); if (!isValidDeclaration(refDecl)) { return false; } boolean isNeverAssigned = refInfo.isNeverAssigned(); // For values that are never assigned, only the references need to be // checked. if (!isNeverAssigned) { Reference refInit = refInfo.getInitializingReference(); if (!isValidInitialization(refInit)) { return false; } if (refDecl != refInit) { Preconditions.checkState(refInit == refSet.get(1)); startingReadRef = 2; } if (!refInfo.isWellDefined()) { return false; } Node value = refInit.getAssignedValue(); Preconditions.checkNotNull(value); boolean isImmutableValueWorthInlining = NodeUtil.isImmutableValue(value) && (value.getType() != Token.STRING || isStringWorthInlining(v, refInfo.references)); boolean isInlinableThisAlias = value.getType() == Token.THIS && !refInfo.isEscaped(); if (!isImmutableValueWorthInlining && !isInlinableThisAlias) { return false; } } for (int i = startingReadRef; i < refSet.size(); i++) { Reference ref = refSet.get(i); if (!isValidReference(ref)) { return false; } } return true; } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> endLine(); } @Override void endCaseBody() { super.endCaseBody(); indent--; endStatement(); } @Override void appendOp(String op, boolean binOp) { if (binOp) { if (getLastChar() != ' ') { append(" "); } append(op); append(" "); } else { append(op); } } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? * {@inheritDoc} */ @Override boolean shouldPreserveExtraBlocks() { // When pretty-printing, always place the statement in its own block // so it is printed on a separate line. This allows breakpoints to be // placed on the statement. return true; } /** * @return The TRY node for the specified CATCH node. */ private Node getTryForCatch(Node n) { return n.getParent().getParent(); } /** * @return Whether the a line break should be added after the specified * BLOCK. */ @Override boolean breakAfterBlockFor(Node n, boolean isStatementContext) { Preconditions.checkState(n.getType() == Token.BLOCK); Node parent = n.getParent(); if (parent != null) { int type = parent.getType(); switch (type) { case Token.DO: // Don't break before 'while' in DO-WHILE statements. return false; case Token.FUNCTION: // FUNCTIONs are handled separately, don't break here. return false; case Token.TRY: // Don't break before catch return n != parent.getFirstChild(); case Token.CATCH: // Don't break before finally return !NodeUtil.hasFinally(getTryForCatch(parent)); case Token.IF: // Don't break before else return n == parent.getLastChild(); } } return true; } } static class CompactCodePrinter extends MappedCodePrinter implements HasGetCode { // The CompactCodePrinter tries to emit just enough newlines to stop there // being lines longer than the threshold. Since the output is going to be // gz

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> p.typeA.getTypesUnderEquality(p.typeB); } }; /** * Merging function for non-equality between types. */ private static final Function<TypePair, TypePair> NE = new Function<TypePair, TypePair>() { public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderInequality(p.typeB); } }; /** * Merging function for strict equality between types. */ private static final Function<TypePair, TypePair> SHEQ = new Function<TypePair, TypePair>() { public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowEquality(p.typeB); } }; /** * Merging function for strict non-equality between types. */ private static final Function<TypePair, TypePair> SHNE = new Function<TypePair, TypePair>() { public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowInequality(p.typeB); } }; /** * Merging function for inequality comparisons between types. */ private final Function<TypePair, TypePair> INEQ = new Function<TypePair, TypePair>() { public TypePair apply(TypePair p) { return new TypePair( getRestrictedWithoutUndefined(p.typeA), getRestrictedWithoutUndefined(p.typeB)); } }; /** * Creates a semantic reverse abstract interpreter. */ SemanticReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { super(convention, typeRegistry); } public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { // Check for the typeof operator. int operatorToken = condition.getType(); switch (operatorToken) { case Token.EQ: case Token.NE

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) { typeOfNode = left; stringNode = right; } else if (right.getType() == Token.TYPEOF && left.getType() == Token.STRING) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().getType() == Token.STRING) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> right, blindScope, condition); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { rightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope if ((leftType != null && leftIsRefineable) || (rightType != null && rightIsRefineable)) { FlowScope informed = blindScope.createChildFlowScope(); if (leftIsRefineable && leftType != null) { declareNameInScope(informed, left, leftType); } if (rightIsRefineable && rightType != null) { declareNameInScope(informed, right, rightType); } return informed; } } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope); if (leftVar == null) { return blindScope; } FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); rightScope = firstPreciserScopeKnowingConditionOutcome( right, rightScope, !condition); StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope); if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) { return blindScope; } JSType type = leftVar.getType().getLeastSupertype(rightVar.getType()); FlowScope informed = blindScope.createChildFlowScope(); informed.inferSlotType(leftVar.getName(), type); return informed;

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>compiler)); } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { if (n.getType() == Token.EXPR_VOID) { n.setType(Token.EXPR_RESULT); reportChange(); } // Remove unused properties to minimize differences between ASTs // produced by the two parsers. if (n.getType() == Token.FUNCTION) { Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null); } normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && n.getType() != Token.LABEL && n.getType() != Token.SWITCH) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && c.getType() != Token.BLOCK) { Node newBlock = new Node(Token.BLOCK, n.getLineno(), n.getCharno()); newBlock.copyInformationFrom(n); n.replaceChild(c, newBlock); if (c.getType() != Token.EMPTY) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations extends NodeTraversal.AbstractPostOrderCallback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } /** * * In the AST that R

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>hino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ public void visit(NodeTraversal t, Node n, Node parent) { int nType = n.getType(); switch (nType) { case Token.NAME: case Token.STRING: String nString = n.getString(); if (nType == Token.NAME && n.getParent().getType() == Token.CALL && "eval".equals(nString)) { n.putBooleanProp(Node.DIRECT_EVAL, true); } if (convention.isConstant(nString)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } break; case Token.FUNCTION: JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null) { // Look for the info on other nodes. if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } break; case Token.OBJECTLIT: if (n.getType() == Token.OBJECTLIT) { for (Node key = n.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); if (key.getJSDocInfo() != null && key.getNext().getType() == Token.FUNCTION) { value.setJSDocInfo(key.getJSDocInfo()); } } } break; } // TODO(johnlenz): Determine if it is possible to simply use the javadoc // everywhere rather than use IS_DISPATCHER. /* * Translate dispatcher info into the property expected node. */ if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) { if (n.getType() == Token.ASSIGN) { Node fnNode = n.getLastChild(); Preconditions.checkState(fnNode.getType() == Token.FUNCTION); fnNode.putBooleanProp(Node.IS_DISPATCHER, true); } } } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType type, JSType castType) { castType = castType.restrictByNotNullOrUndefined(); type = type.restrictByNotNullOrUndefined(); if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) { compiler.report( t.makeError(n, INVALID_CAST, castType.toString(), type.toString())); registerMismatch(type, castType); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. */ void expectUndeclaredVariable(String sourceName, Node n, Node parent, Var var, String variableName, JSType newType) { boolean allowDupe = false; if (n.getType() == Token.GETPROP) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a //

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> native type. if (var.input == null) { n.setJSType(varType); if (parent.getType() == Token.VAR) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.getType() == Token.FUNCTION); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().getType() == Token.EXPR_RESULT) || !newType.equals(varType)) { compiler.report( JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } } /** * Expect that all properties on interfaces that this type implements are * implemented. */ void expectAllInterfacePropertiesImplemented(FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { if (!instance.hasProperty(prop)) { Node source = type.getSource(); Preconditions.checkNotNull(source); String sourceName = (String) source.getProp(Node.SOURCENAME_PROP); sourceName = sourceName == null ? "" : sourceName; compiler.report(JSError.make(sourceName, source, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implemented.toString(), instance.toString())); registerMismatch(instance, implemented); } } } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); } private void mismatch(Node

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.getType() == Token.GETPROP) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type instanceof FunctionType) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } overrideDefines(collectDefines(root, namespace)); } private void overrideDefines(Map<String, DefineInfo> allDefines) { boolean changed = false; for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) { String defineName = def.getKey(); DefineInfo info = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set<String> unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation List<Name> allDefines = Lists.newArrayList

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(); for (Name name : namespace.getNameIndex().values()) { if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( name.declaration.sourceName, name.declaration.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else if (name.refs != null) { for (Ref ref : name.refs) { Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.getType() == Token.VAR && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map<String, DefineInfo> assignableDefines; private final Map<String, DefineInfo> allDefines; private final Map<Node, RefInfo> allRefInfo; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque<Integer> assignAllowed; CollectDefines(AbstractCompiler compiler, List<Name> listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap();

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> assignableDefines = Maps.newHashMap(); assignAllowed = new ArrayDeque<Integer>(); assignAllowed.push(1); // Create a map of references to defines keyed by node for easy lookup allRefInfo = Maps.newHashMap(); for (Name name : listOfDefines) { if (name.declaration != null) { allRefInfo.put(name.declaration.node, new RefInfo(name.declaration, name)); } if (name.refs != null) { for (Ref ref : name.refs) { // If there's a TWIN def, only put one of the twins in. if (ref.getTwin() == null || !ref.getTwin().isSet()) { allRefInfo.put(ref.node, new RefInfo(ref, name)); } } } } } /** * Get a map of {@link DefineInfo} structures, keyed by the name of * the define. */ Map<String, DefineInfo> getAllDefines() { return allDefines; } /** * Keeps track of whether the traversal is in a conditional branch. * We traverse all nodes of the parse tree. */ public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { updateAssignAllowedStack(n, true); return true; } public void visit(NodeTraversal t, Node n, Node parent) { RefInfo refInfo = allRefInfo.get(n); if (refInfo != null) { Ref ref = refInfo.ref; Name name = refInfo.name; String fullName = name.fullName(); switch (ref.type) { case SET_FROM_GLOBAL: case SET_FROM_LOCAL: Node valParent = getValueParent(ref); Node val = valParent.getLastChild(); if (valParent.getType() == Token.ASSIGN && name.isSimpleName() && name.declaration == ref) { // For defines, it's an error if a simple name is assigned // before it's declared compiler.report( t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.getType() == Token.ASSIGN) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.getType() == Token.NAME); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.getType() == Token.CALL) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, expecially because // the intended use of defines is with config_files, where // all the

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( t.makeError(value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info);

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().getType() == Token.VAR ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param info Represents the define variable. * @param t The current traversal. */ private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) { info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE, t.getLineNumber(), t.getSourceName())); } /** * A simple data structure for associating a Ref with the name * that it references. */ private static class RefInfo { final Ref ref; final Name name; RefInfo(Ref ref, Name name) { this.ref = ref; this.name = name; } } } /** * A simple class for storing information about a define. * Gathers the initial value, the last assigned value, and whether * the define can be safely assigned a new value. */ private static final class DefineInfo { public final Node initialValueParent; public final Node initialValue; private Node lastValue; private boolean isAssignable; private String reasonNotAssignable; /** * Initializes a define. */ public DefineInfo(Node initialValue, Node initialValueParent) { this

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. * */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb;

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.getType() == Token.FUNCTION) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block traverseBranch(n.getFirstChild(), n); traverseBranch(n.getFirstChild().getNext().getNext(), n); break; case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); traverseBranch(body, n); popScope(); } /** Examines the functions stack for

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>emptySet(); replaceStringsFunctionDescriptions = Collections.emptyList(); replaceStringsPlaceholderToken = ""; // Output printInputDelimiter = false; prettyPrint = false; lineBreak = false; reportPath = null; tracer = TracerMode.OFF; colorizeErrorOutput = false; errorFormat = ErrorFormat.SINGLELINE; warningsGuard = null; debugFunctionSideEffectsPath = null; jsOutputFile = ""; nameReferenceReportPath = null; nameReferenceGraphPath = null; } /** * Returns the map of define replacements. */ public Map<String, Node> getDefineReplacements() { Map<String, Node> map = Maps.newHashMap(); for (Map.Entry<String, Object> entry : defineReplacements.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Boolean) { map.put(name, ((Boolean) value).booleanValue() ? new Node(Token.TRUE) : new Node(Token.FALSE)); } else if (value instanceof Integer) { map.put(name, Node.newNumber(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, Node.newNumber(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, Node.newString((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void setDefineToBooleanLiteral(String defineName, boolean value) { defineReplacements.put(defineName, new Boolean(value)); } /** * Sets the value of the {@code @define} variable in JS to a * String literal. */ public void setDefineToStringLiteral(String defineName, String value) { defineReplacements.put(defineName, value); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToNumberLiteral(String defineName, int value) { defineReplacements.put(defineName, new Integer(value)); } /** * Sets the

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>; // f scope 'e' // } // otherwise 'var e = 1' would be rewritten as 'e = 1'. // TODO(johnlenz): Introduce a seperate scope for catch nodes. removeDuplicateDeclarations(root); new PropogateConstantAnnotations(compiler, assertOnChange) .process(externs, root); } public static class PropogateConstantAnnotations extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; public PropogateConstantAnnotations( AbstractCompiler compiler, boolean forbidChanges) { this.compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.getType() == Token.NAME) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if ((info != null && info.isConstant()) && !n.getBooleanProp(Node.IS_CONSTANT_NAME)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " gramps:" + n.getParent().getParent().toStringTree()); } // Even though the AST has changed (an annotation was added), // the annotations are not compared so don't report the change. // reportCodeChange("constant annotation"); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String,Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; if (NodeUtil.isConstantName(n) || compiler.getCodingConvention().isConstant(n.getString())) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name " + name + " is not annotated as constant."); } else { Preconditions.checkState(expectedConst == isConst, "The name " + name + " should not be annotated as constant."); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name " + name + " is not consistently annotated as " + "constant."); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); Node empty = new Node(Token.EMPTY); empty.copyInformationFrom(n); n.addChildBefore(empty, expr); n.addChildAfter(empty.cloneNode(), expr); reportCodeChange("WHILE node"); } break; case Token.FUNCTION: normalizeFunctionDeclaration(n); break; } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ private void normalizeFunctionDeclaration(Node n) { Preconditions.checkState(n.getType() == Token.FUNCTION); if (!NodeUtil.isFunctionExpression(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { rewriteFunctionDeclaration(n); } } /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x =

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ private void rewriteFunctionDeclaration(Node n) { // Prepare a spot for the function. Node oldNameNode = n.getFirstChild(); Node fnNameNode = oldNameNode.cloneNode(); Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno()); var.copyInformationFrom(n); // Prepare the function oldNameNode.setString(""); // Move the function Node parent = n.getParent(); parent.replaceChild(n, var); fnNameNode.addChildToFront(n); reportCodeChange("Function declaration"); } /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.LABEL) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) { extractForInitializer(n, null, null); } // Only inspect the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock(n)) { splitVarDeclarations(n); } if (n.getType() == Token.FUNCTION) { moveNamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.getType() == Token.LABEL); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>FOR: case Token.WHILE: case Token.DO: return; default: Node block = new Node(Token.BLOCK); block.copyInformationFrom(last); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (!NodeUtil.isForIn(c) && c.getFirstChild().getType() != Token.EMPTY) { Node init = c.getFirstChild(); Node empty = new Node(Token.EMPTY); empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.getType() == Token.VAR) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.getType() == Token.VAR) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = new Node( Token.VAR, name, n.getLineno(), n.getCharno()); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent().getType() == Token.FUNCTION); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Readd the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverse(root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { Preconditions.checkState(n.getType() == Token.NAME); Var v = s.getVar(name); // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().getType() == Token.CATCH) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The EcmaScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e': e}) {}" so that // "var e" would refer to the scope variable, but any following // reference would still refer to "e" of the catch expression. // Until we have support for this disallow it. // Currently the Scope object adds the catch expression to the // function scope, which is technically not true but a good // approximation for most uses. // TODO(johnlenz): Consider improving how scope handles catch // expression. // Use the name of the var before it was made unique. name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( name); compiler.report( JSError.make( NodeUtil.getSourceName(nodeWithLineNumber), nodeWith

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>LineNumber, CATCH_BLOCK_VAR_ERROR, name)); } else if (v != null && parent.getType() == Token.FUNCTION) { if (v.getParentNode().getType() == Token.VAR) { s.undeclare(v); s.declare(name, n, n.getJSType(), v.input); replaceVarWithAssignment(v.getNameNode(), v.getParentNode(), v.getParentNode().getParent()); } } else if (parent.getType() == Token.VAR) { Preconditions.checkState(parent.hasOneChild()); replaceVarWithAssignment(n, parent, gramps); } } /** * Remove the parent VAR. There are three cases that need to be handled: * 1) "var a = b;" which is replaced with "a = b" * 2) "label:var a;" which is replaced with "label:;". Ideally, the * label itself would be removed but that is not possible in the * context in which "onRedeclaration" is called. * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." * Cases we don't need to handle are VARs with multiple children, * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = new Node(Token.ASSIGN, n, value); replacement.copyInformationFrom(parent); gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.getType() == Token.FOR) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for (var a;;)..." as those are moved out // as part of the other normalizations. parent.removeChild(n); gramps.replaceChild(parent, n); } else { Preconditions.checkState(gramps.getType() == Token.LABEL); // We should never get here. LABELs with a single VAR statement should // already have been normalized to have a BLOCK. throw new IllegalStateException("Unexpected LABEL"); } } reportCodeChange("Duplicate VAR declaration"); } } /** * A simple class that causes scope to be created. */ private final class ScopeTicklingCallback implements NodeTraversal.ScopedCallback { @Override public void enterScope(NodeTraversal t) { // Cause the scope to be created, which will cause duplicate // to be found. t.getScope(); } @Override public void exitScope(NodeTraversal t) { // Nothing to do. } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Nothing to do. } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>er.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); // Verify the symbol table. ErrorManager symbolTableErrorManager = new BlackHoleErrorManager(compiler); Node expectedRoot = parseExpectedJs(expected); expectedRoot.detachFromParent(); JSError[] stErrors = symbolTableErrorManager.getErrors(); if (expectedSymbolTableError != null) { assertEquals("There should be one error.", 1, stErrors.length); assertEquals(expectedSymbolTableError, stErrors[0].getType()); } else { assertEquals("Unexpected symbol table error(s): " + Joiner.on("\n").join(stErrors), 0, stErrors.length); } if (warning == null) { assertEquals( "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), 0, aggregateWarningCount); } else { assertEquals("There should be one warning, repeated " + numRepetitions + " time(s).", numRepetitions, aggregateWarningCount); for (int i = 0; i < numRepetitions; ++i) { JSError[] warnings = errorManagers[i].getWarnings(); JSError actual = warnings[0]; assertEquals(warning, actual.getType()); // Make sure that source information is always provided. if (!allowSourcelessWarnings) { assertTrue("Missing source file name in warning", actual.sourceName != null && !actual.sourceName.isEmpty()); assertTrue("Missing line number in warning", -1 != actual.lineNumber); assertTrue("Missing char number in warning", -1 != actual.getCharno()); } if (description != null) { assertEquals(description, actual.description); } } } if (normalizeEnabled) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRootClone, mainRootClone); } if (mainRootClone.checkTreeEqualsSilent(mainRoot)) { assertFalse( "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue("compiler.reportCodeChange() should have been called", hasCodeChanged); } if (compareAsTree) {

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> String explanation = expectedRoot.checkTreeEquals(mainRoot); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } else if (expected != null) { assertEquals( Joiner.on("").join(expected), compiler.toSource(mainRoot)); } // Verify normalization is not invalidated. Node normalizeCheckRootClone = root.cloneTree(); Node normalizeCheckExternsRootClone = root.getFirstChild(); Node normalizeCheckMainRootClone = root.getLastChild(); new PrepareAst(compiler).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); String explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Node structure normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); // TODO(johnlenz): enable this for most test cases. // Currently, this invalidates test for while-loops, for-loop // initializers, and other naming. However, a set of code // (FoldConstants, etc) runs before the Normalize pass, so this can't be // force on everywhere. if (normalizeEnabled) { new Normalize(compiler, true).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } } else { String errors = ""; for (JSError actualError : compiler.getErrors()) { errors += actualError.description + "\n"; } assertEquals("There should be one error. " + errors, 1, compiler.getErrorCount()); assertEquals(errors, error, compiler.getErrors()[0].getType()); } } /** * Parses expected js inputs and returns the root of the parse tree. */ private Node parseExpectedJs(String

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType INVALID_FUNCTION_DECL = DiagnosticType.error("JSC_INVALID_FUNCTION_DECL", "Syntax error: function declaration must have a name"); private CompilerInput synthesizedExternsInput = null; private Node synthesizedExternsRoot = null; private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.NAME) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(NodeUtil.isFunction(parent)); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isFunctionExpression(parent)) { t.report(n, INVALID_FUNCTION_DECL); } return; } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { t.report(n, UNDEFINED_VAR_ERROR, varName); if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { // Create a

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> new variable in a synthetic script. This will prevent // subsequent compiler passes from crashing. Node nameNode = Node.newString(Token.NAME, varName); getSynthesizedExternsRoot().addChildToBack( new Node(Token.VAR, nameNode)); scope.getGlobalScope().declare(varName, nameNode, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (!sanityCheck && scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { switch

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.GETPROP: case Token.LP: // These are okay. break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = compiler.newExternInput("{SyntheticVarsDeclar}"); } return synthesizedExternsInput; } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) { CompilerInput synthesizedExterns = getSynthesizedExternsInput(); synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); } return synthesizedExternsRoot; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Root) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { JSDocInfo info; switch (n.getType()) { case Token.SCRIPT: case Token.VAR: // @notypecheck info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { return false; } break; case Token.FUNCTION: // @notypecheck info = n.getJSDocInfo(); info = (info == null) ? parent.getJSDocInfo() : info; if (info != null && info.isNoTypeCheck()) { return false; } // normal type checking final TypeCheck outerThis = this; final Scope outerScope = t.getScope(); final FunctionType functionType = (FunctionType) n.getJSType(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // re

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>declarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.REF_SPECIAL: ensureTyped(t, n); break; case Token.GET_REF: ensureTyped(t, n, getJSType(n.getFirstChild())); break; case Token.NULL: ensure

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Typed(t, n, NULL_TYPE); break; case Token.NUMBER: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, NUMBER_TYPE); } else { typeable = false; } break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.STRING: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, STRING_TYPE); } else { typeable = false; } break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.getType() == Token.ASSIGN && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild());

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> if (!childType.matchesInt32Context()) { t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } t.report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotVoid( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotVoid( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND:

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: if (!isReference(n.getFirstChild())) { t.report(n, BAD_DELETE); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.getType() == Token.OBJECTLIT) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } break; default: t.report(n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.getType() == Token.GETPROP) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope(), typeRegistry), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate( t.getScope(), typeRegistry)); return; } // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType(); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( object

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>JsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** * Returns true if any type in the chain has an implictCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // TODO(user): We're not 100% confident that type-checking works, // so we return quietly if the unknown type is a superclass of this type. // Remove this check

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_SUPERCLASS_PROPERTY, propertyName, topInstanceType.toString())); } if (!declaredOverride) { // there's no @override to check return; } // @override is present and we have to check that it is ok if (superClassHasProperty) { // there is a superclass implementation JSType superClassPropType = superClass.getPrototype().getPropertyType(propertyName); if (!propertyType.canAssignTo(superClassPropType)) { compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superClassPropType.toString(), propertyType.toString())); } } else if (!foundInterfaceProperty) { // there is no superclass nor interface implementation compiler.report( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isOrdinaryFunction() && !(rvalue.isQualifiedName() && rvalue.getQualifiedName().equals(abstractMethodName))) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_MEMBERS_ONLY, abstractMethodName)); } if (assign.getLastChild().getType() == Token.FUNCTION && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits an ASSIGN node for cases such as * <pre> * object.property = ...; * </pre> * that have an {@code @type} annotation. */ private void visitAnnotatedAssignGetprop(NodeTraversal t, Node assign, JS

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Type type, Node object, String property, Node rvalue) { // verifying that the rvalue has the correct type validator.expectCanAssignToPropertyOf(t, assign, getJSType(rvalue), type, object, property); } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.LP || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.getType() == Token.ASSIGN) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotVoid(t, n, childType, "undefined has no properties", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n); return; } checkPropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Make sure that the access of this property is ok. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { ObjectType objectType = childType.dereference(); if (objectType != null) { JSType propType = getJSType(n); if ((!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) && propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { t.report(n, INEXISTENT_ENUM_ELEMENT, propName); } else if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { t.report(n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirstChild(), true)); } } } } else { // TODO(nicksantos): might want to flag the access on a non object when // it's impossible to get a property from this type. } } /**

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } if (info != null && info.hasEnumParameterType()) { // var.getType() can never be null, this would indicate a bug in the // scope creation logic. checkEnumInitializer( t, value, info.getEnumParameterType().evaluate(t.getScope(), typeRegistry)); } else if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); FunctionType type = getFunctionType(constructor); if (type != null && type.isConstructor()) { visitParameterList(t, n, type); ensureTyped(t, n, type.getInstanceType()); } else { // TODO(user): add support for namespaced objects. if (constructor.getType() != Token.GETPROP) { // TODO(user): make the constructor node have lineno/charno // and use constructor for a more precise error indication. // It seems that GETPROP nodes are missing this information. Node line; if (constructor.getLineno() < 0 || constructor.getCharno() < 0) { line = n; } else { line = constructor; } t.report(line, NOT_A_CONSTRUCTOR); } ensureTyped(t, n); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) {

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { t.report(left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { t.report(right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break; default: t.report(n, UNEXPECTED_TOKEN, Node.tokenToName(op)); } ensureTyped(t, n); } /** * <p>Checks the initializer of an enum. An enum can be initialized with an * object literal whose values must be subtypes of the declared enum element * type, or by copying another enum.</p> * * <p>In the case of an enum copy, we verify that the enum element type of the * enum used for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Examples:</p> * <pre>var myEnum = {FOO: ..., BAR: ...}; * var myEnum = myOtherEnum;</pre> * * @param value the value used for initialization of the enum * @param primitiveType The type of each element of the enum. */ private void checkEnumInitializer( NodeTraversal t, Node value, JSType primitiveType) { if (value.getType() == Token.OBJECTLIT) { // re-using value as the value of the object literal and advancing twice value = value.getFirstChild(); value = (value == null) ? null : value.getNext(); while (value != null) { // the value's type

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> must be assignable to the enum's primitive type validator.expectCanAssignTo(t, value, getJSType(value), primitiveType, "element type must match enum's type"); // advancing twice value = value.getNext(); value = (value == null) ? null : value.getNext(); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, primitiveType, "incompatible enum element types"); } else { // The error condition is handled in TypedScopeCreator. } } /** * This predicate is used to determine if the node represents an expression * that is a Reference according to JavaScript definitions. * * @param n The node being checked. * @return true if the sub-tree n is a reference, false otherwise. */ private static boolean isReference(Node n) { switch (n.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.NAME: return true; default: return false; } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } /** * Gets the type of the node or

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.getType() != Token.FUNCTION || type instanceof FunctionType || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n.getType() == Token.GETPROP ? n.getLastChild().getString() : "(missing)"; compiler.report( t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of nodes typed by the type checker. * @return a number between 0.0 and 100.0 */ double getTypedPercent() { int total = nullCount + unknownCount + typedCount; if (total == 0) { return 0.0; } else { return (100.0 * typedCount) / total; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>OverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); } } return irNode; } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (irNode.getType() != Token.BLOCK) { if (irNode.getType() == Token.EMPTY) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = new Node(Token.BLOCK, irNode, irNode.getLineno(), irNode.getCharno()); irNode = newBlock; } } return irNode; } private Node transform(AstNode node) { String jsDoc = node.getJsDoc(); NodeWithJsDoc nodeWithJsDoc = null; if (jsDoc != null) { nodeWithJsDoc = new NodeWithJsDoc(); nodesWithJsDoc.put(jsDoc, nodeWithJsDoc); } Node irNode = justTransform(node); if (nodeWithJsDoc != null) { nodeWithJsDoc.node = irNode; } // If we have a named function, set the position to that of the name. if (irNode.getType() == Token.FUNCTION && irNode.getFirstChild().getLineno() != -1) { irNode.setLineno(irNode.getFirstChild().getLineno()); irNode.setCharno(irNode.getFirstChild().getCharno()); } else { if (irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); } } return irNode; } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Index - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = new Node(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) { node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret = transform(n); if (ret.getType() == Token.STRING) { ret.putBooleanProp(Node.QUOTED_PROP, true); } else if (ret.getType() == Token.NAME) { ret.setType(Token.STRING); } return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = new Node(Token.ARRAYLIT); int skipCount = 0; for (AstNode child : literalNode.getElements()) { Node c = transform(child); if (c.getType() == Token.EMPTY) { skipCount++; } node.addChildToBack(c); } if (skipCount > 0) { int[] skipIndexes = new int[skipCount]; int i = 0; int j = 0; for (Node child : node.children()) { if (child.getType() == Token.EMPTY) { node.removeChild(child); skipIndexes[j]

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> = i; j++; } i++; } node.putProp(Node.SKIP_INDEXES_PROP, skipIndexes); } return node; } @Override Node processAssignment(Assignment assignmentNode) { return processInfixExpression(assignmentNode); } @Override Node processAstRoot(AstRoot rootNode) { Node node = new Node(Token.SCRIPT); for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * Ecma-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return (nType == Token.EXPR_RESULT || nType == Token.EXPR_VOID) && n.getFirstChild().getType() == Token.STRING && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = new Node(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = new Node(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { node.addChildToBack(transform(clauseNode.getCatchCondition())); } else { Node catchCondition = new Node(Token.EMPTY); // Old Rhino used the position of the catchVar as the position // for the (nonexistent) error being caught. catchCondition.setLineno(catchVar.getLineno()); int clauseAbsolutePosition = position2charno(catchVar.getAbsolutePosition()); catchCondition.setCharno(clauseAbsolutePosition); node.addChildToBack(catchCondition); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return new Node( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = new Node(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return new Node( Token.DO, transformBlock(loopNode.getBody()), transform(loopNode.getCondition())); } @Override Node processElementGet(ElementGet getNode) { return new Node( Token.GETELEM, transform(getNode.getTarget()), transform(getNode.getElement())); } @Override Node processEmptyExpression(EmptyExpression exprNode) { Node node = new Node(Token.EMPTY); return node; } @Override Node processExpressionStatement(ExpressionStatement statementNode) {

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Node node = new Node(transformTokenType(statementNode.getType())); node.addChildToBack(transform(statementNode.getExpression())); return node; } @Override Node processForInLoop(ForInLoop loopNode) { return new Node( Token.FOR, transform(loopNode.getIterator()), transform(loopNode.getIteratedObject()), transformBlock(loopNode.getBody())); } @Override Node processForLoop(ForLoop loopNode) { Node node = new Node( Token.FOR, transform(loopNode.getInitializer()), transform(loopNode.getCondition()), transform(loopNode.getIncrement())); node.addChildToBack(transformBlock(loopNode.getBody())); return node; } @Override Node processFunctionCall(FunctionCall callNode) { Node node = new Node(transformTokenType(callNode.getType()), transform(callNode.getTarget())); for (AstNode child : callNode.getArguments()) { node.addChildToBack(transform(child)); } int leftParamPos = callNode.getAbsolutePosition() + callNode.getLp(); node.setLineno(callNode.getLineno()); node.setCharno(position2charno(leftParamPos)); return node; } @Override Node processFunctionNode(FunctionNode functionNode) { Name name = functionNode.getFunctionName(); Boolean isUnnamedFunction = false; if (name == null) { name = new Name(); name.setIdentifier(""); isUnnamedFunction = true; } Node node = new Node(Token.FUNCTION); node.putProp(Node.SOURCENAME_PROP, functionNode.getSourceName()); Node newName = transform(name); if (isUnnamedFunction) { // Old Rhino tagged the empty name node with the line number of the // declaration. newName.setLineno(functionNode.getLineno()); // TODO(bowdidge) Mark line number of paren correctly. // Same problem as below - the left paren might not be on the // same line as the function keyword. int lpColumn = functionNode.getAbsolutePosition() + functionNode.getLp(); newName.setCharno(position2charno(lpColumn

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>)); } node.addChildToBack(newName); Node lp = new Node(Token.LP); // The left paren's complicated because it's not represented by an // AstNode, so there's nothing that has the actual line number that it // appeared on. We know the paren has to appear on the same line as the // function name (or else a semicolon will be inserted.) If there's no // function name, assume the paren was on the same line as the function. // TODO(bowdidge): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLineno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { lp.addChildToBack(transform(param)); } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = new Node(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = new Node( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); // Set the line number here so we can fine-tune it in ways transform // doesn't do. n.setLineno(exprNode.getLineno()); // Position in new ASTNode is to start of expression, but old-fashioned // line numbers from Node reference the operator token.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Add the offset // to the operator to get the correct character number. n.setCharno(position2charno(exprNode.getAbsolutePosition() + exprNode.getOperatorPosition())); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return new Node(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return Node.newString(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = new Node(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = new Node(Token.LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) { return Node.newString(Token.NAME, nameNode.getIdentifier()); } @Override Node processNewExpression(NewExpression exprNode) { return processFunctionCall(exprNode); } @Override Node processNumberLiteral(NumberLiteral literalNode) { Node newNode = Node.newNumber(literalNode.getNumber()); return newNode; } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = new Node(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (el.isGetter()) { reportGetter(el); } else if (el.isSetter()) { reportSetter(el); } else { node.addChildToBack(transformAsString(el.getLeft())); node.addChildToBack(transform(el.getRight())); } } return node; } @Override

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return new Node( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = Node.newString(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); Node node = new Node(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = Node.newString(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = new Node(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { Node n = Node.newString(literalNode.getValue()); return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = new Node(Token.DEFAULT); } else { AstNode expr = caseNode.getExpression(); node = new Node(Token.CASE, transform(expr)); } Node block = new Node(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>setCharno(position2charno(caseNode.getAbsolutePosition())); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = new Node(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return new Node(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = new Node(Token.TRY, transformBlock(statementNode.getTryBlock())); Node block = new Node(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if (lineSet == false) { block.setLineno(cc.getLineno()); lineSet = true; } block.addChildToBack(transform(cc)); } node.addChildToBack(block); AstNode finallyBlock = statementNode.getFinallyBlock(); if (finallyBlock != null) { node.addChildToBack(transformBlock(finallyBlock)); } // If we didn't set the line on the catch clause, then // we've got an empty catch clause. Set its line to be the same // as the finally block (to match Old Rhino's behavior.) if ((lineSet == false) && (finallyBlock != null)) { block.setLineno(finallyBlock.getLineno()); } return node; } @Override Node processUnaryExpression(UnaryExpression exprNode) { Node node = new Node(transformTokenType(exprNode.getType()), transform(exprNode.getOperand())); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true);

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> } return node; } @Override Node processVariableDeclaration(VariableDeclaration declarationNode) { Node node = new Node(Token.VAR); for (VariableInitializer child : declarationNode.getVariables()) { node.addChildToBack(transform(child)); } return node; } @Override Node processVariableInitializer(VariableInitializer initializerNode) { Node node = transform(initializerNode.getTarget()); if (initializerNode.getInitializer() != null) { node.addChildToBack(transform(initializerNode.getInitializer())); node.setLineno(node.getLineno()); } return node; } @Override Node processWhileLoop(WhileLoop loopNode) { return new Node( Token.WHILE, transform(loopNode.getCondition()), transformBlock(loopNode.getBody())); } @Override Node processWithStatement(WithStatement statementNode) { return new Node( Token.WITH, transform(statementNode.getExpression()), transformBlock(statementNode.getStatement())); } @Override Node processIllegalToken(AstNode node) { errorReporter.error( "Unsupported syntax: " + com.google.javascript.jscomp.mozilla.rhino.Token.typeToName( node.getType()), sourceName, node.getLineno(), "", 0); return new Node(Token.EMPTY); } void reportDestructuringAssign(AstNode node) { errorReporter.error( "destructuring assignment forbidden", sourceName, node.getLineno(), "", 0); } void reportGetter(AstNode node) { errorReporter.error( "getters are not supported in Internet Explorer", sourceName, node.getLineno(), "", 0); } void reportSetter(AstNode node) { errorReporter.error( "setters are not supported in Internet Explorer", sourceName, node.getLineno(), "", 0); } } private static int transformTokenType(int token) { switch (token) { case com.google.javascript.jscomp.mozilla.rhino.Token.ERROR: return Token.ERROR; case com.google.javascript.jscomp.mozilla.rhino.Token.EOF:

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> return Token.EOF; case com.google.javascript.jscomp.mozilla.rhino.Token.EOL: return Token.EOL; case com.google.javascript.jscomp.mozilla.rhino.Token.ENTERWITH: return Token.ENTERWITH; case com.google.javascript.jscomp.mozilla.rhino.Token.LEAVEWITH: return Token.LEAVEWITH; case com.google.javascript.jscomp.mozilla.rhino.Token.RETURN: return Token.RETURN; case com.google.javascript.jscomp.mozilla.rhino.Token.GOTO: return Token.GOTO; case com.google.javascript.jscomp.mozilla.rhino.Token.IFEQ: return Token.IFEQ; case com.google.javascript.jscomp.mozilla.rhino.Token.IFNE: return Token.IFNE; case com.google.javascript.jscomp.mozilla.rhino.Token.SETNAME: return Token.SETNAME; case com.google.javascript.jscomp.mozilla.rhino.Token.BITOR: return Token.BITOR; case com.google.javascript.jscomp.mozilla.rhino.Token.BITXOR: return Token.BITXOR; case com.google.javascript.jscomp.mozilla.rhino.Token.BITAND: return Token.BITAND; case com.google.javascript.jscomp.mozilla.rhino.Token.EQ: return Token.EQ; case com.google.javascript.jscomp.mozilla.rhino.Token.NE: return Token.NE; case com.google.javascript.jscomp.mozilla.rhino.Token.LT: return Token.LT; case com.google.javascript.jscomp.mozilla.rhino.Token.LE: return Token.LE; case com.google.javascript.jscomp.mozilla.rhino.Token.GT: return Token.GT; case com.google.javascript.jscomp.mozilla.rhino.Token.GE: return Token.GE; case com.google.javascript.jscomp.mozilla.rhino.Token.LSH: return Token.LSH; case com.google.javascript.jscomp.mozilla.rhino.Token

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.RSH: return Token.RSH; case com.google.javascript.jscomp.mozilla.rhino.Token.URSH: return Token.URSH; case com.google.javascript.jscomp.mozilla.rhino.Token.ADD: return Token.ADD; case com.google.javascript.jscomp.mozilla.rhino.Token.SUB: return Token.SUB; case com.google.javascript.jscomp.mozilla.rhino.Token.MUL: return Token.MUL; case com.google.javascript.jscomp.mozilla.rhino.Token.DIV: return Token.DIV; case com.google.javascript.jscomp.mozilla.rhino.Token.MOD: return Token.MOD; case com.google.javascript.jscomp.mozilla.rhino.Token.NOT: return Token.NOT; case com.google.javascript.jscomp.mozilla.rhino.Token.BITNOT: return Token.BITNOT; case com.google.javascript.jscomp.mozilla.rhino.Token.POS: return Token.POS; case com.google.javascript.jscomp.mozilla.rhino.Token.NEG: return Token.NEG; case com.google.javascript.jscomp.mozilla.rhino.Token.NEW: return Token.NEW; case com.google.javascript.jscomp.mozilla.rhino.Token.DELPROP: return Token.DELPROP; case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOF: return Token.TYPEOF; case com.google.javascript.jscomp.mozilla.rhino.Token.GETPROP: return Token.GETPROP; case com.google.javascript.jscomp.mozilla.rhino.Token.SETPROP: return Token.SETPROP; case com.google.javascript.jscomp.mozilla.rhino.Token.GETELEM: return Token.GETELEM; case com.google.javascript.jscomp.mozilla.rhino.Token.SETELEM: return Token.SETELEM; case com.google.javascript.jscomp.mozilla.rhino.Token.CALL: return Token.CALL; case com.google.javascript.jscomp.mozilla

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.rhino.Token.NAME: return Token.NAME; case com.google.javascript.jscomp.mozilla.rhino.Token.NUMBER: return Token.NUMBER; case com.google.javascript.jscomp.mozilla.rhino.Token.STRING: return Token.STRING; case com.google.javascript.jscomp.mozilla.rhino.Token.NULL: return Token.NULL; case com.google.javascript.jscomp.mozilla.rhino.Token.THIS: return Token.THIS; case com.google.javascript.jscomp.mozilla.rhino.Token.FALSE: return Token.FALSE; case com.google.javascript.jscomp.mozilla.rhino.Token.TRUE: return Token.TRUE; case com.google.javascript.jscomp.mozilla.rhino.Token.SHEQ: return Token.SHEQ; case com.google.javascript.jscomp.mozilla.rhino.Token.SHNE: return Token.SHNE; case com.google.javascript.jscomp.mozilla.rhino.Token.REGEXP: return Token.REGEXP; case com.google.javascript.jscomp.mozilla.rhino.Token.BINDNAME: return Token.BINDNAME; case com.google.javascript.jscomp.mozilla.rhino.Token.THROW: return Token.THROW; case com.google.javascript.jscomp.mozilla.rhino.Token.RETHROW: return Token.RETHROW; case com.google.javascript.jscomp.mozilla.rhino.Token.IN: return Token.IN; case com.google.javascript.jscomp.mozilla.rhino.Token.INSTANCEOF: return Token.INSTANCEOF; case com.google.javascript.jscomp.mozilla.rhino.Token.LOCAL_LOAD: return Token.LOCAL_LOAD; case com.google.javascript.jscomp.mozilla.rhino.Token.GETVAR: return Token.GETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.SETVAR: return Token.SETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH_SCOPE: return Token.CATCH_SCOPE; case com

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.google.javascript.jscomp.mozilla.rhino.Token.ENUM_INIT_KEYS: return Token.ENUM_INIT_KEYS; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_INIT_VALUES: return Token.ENUM_INIT_VALUES; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_NEXT: return Token.ENUM_NEXT; case com.google.javascript.jscomp.mozilla.rhino.Token.ENUM_ID: return Token.ENUM_ID; case com.google.javascript.jscomp.mozilla.rhino.Token.THISFN: return Token.THISFN; case com.google.javascript.jscomp.mozilla.rhino.Token.RETURN_RESULT: return Token.RETURN_RESULT; case com.google.javascript.jscomp.mozilla.rhino.Token.ARRAYLIT: return Token.ARRAYLIT; case com.google.javascript.jscomp.mozilla.rhino.Token.OBJECTLIT: return Token.OBJECTLIT; case com.google.javascript.jscomp.mozilla.rhino.Token.GET_REF: return Token.GET_REF; case com.google.javascript.jscomp.mozilla.rhino.Token.SET_REF: return Token.SET_REF; case com.google.javascript.jscomp.mozilla.rhino.Token.DEL_REF: return Token.DEL_REF; case com.google.javascript.jscomp.mozilla.rhino.Token.REF_CALL: return Token.REF_CALL; case com.google.javascript.jscomp.mozilla.rhino.Token.REF_SPECIAL: return Token.REF_SPECIAL; case com.google.javascript.jscomp.mozilla.rhino.Token.DEFAULTNAMESPACE: return Token.DEFAULTNAMESPACE; case com.google.javascript.jscomp.mozilla.rhino.Token.ESCXMLTEXT: return Token.ESCXMLTEXT; case com.google.javascript.jscomp.mozilla.rhino.Token.ESCXMLATTR: return Token.ESCXMLATTR; case com.google.javascript.jscomp.mozilla.rhino.Token.REF_MEMBER: return Token.REF_MEMBER; case

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> com.google.javascript.jscomp.mozilla.rhino.Token.REF_NS_MEMBER: return Token.REF_NS_MEMBER; case com.google.javascript.jscomp.mozilla.rhino.Token.REF_NAME: return Token.REF_NAME; case com.google.javascript.jscomp.mozilla.rhino.Token.REF_NS_NAME: return Token.REF_NS_NAME; case com.google.javascript.jscomp.mozilla.rhino.Token.TRY: return Token.TRY; case com.google.javascript.jscomp.mozilla.rhino.Token.SEMI: return Token.SEMI; case com.google.javascript.jscomp.mozilla.rhino.Token.LB: return Token.LB; case com.google.javascript.jscomp.mozilla.rhino.Token.RB: return Token.RB; case com.google.javascript.jscomp.mozilla.rhino.Token.LC: return Token.LC; case com.google.javascript.jscomp.mozilla.rhino.Token.RC: return Token.RC; case com.google.javascript.jscomp.mozilla.rhino.Token.LP: return Token.LP; case com.google.javascript.jscomp.mozilla.rhino.Token.RP: return Token.RP; case com.google.javascript.jscomp.mozilla.rhino.Token.COMMA: return Token.COMMA; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN: return Token.ASSIGN; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_BITOR: return Token.ASSIGN_BITOR; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_BITXOR: return Token.ASSIGN_BITXOR; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_BITAND: return Token.ASSIGN_BITAND; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_LSH: return Token.ASSIGN_LSH; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_RSH:

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> return Token.ASSIGN_RSH; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_URSH: return Token.ASSIGN_URSH; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_ADD: return Token.ASSIGN_ADD; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_SUB: return Token.ASSIGN_SUB; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_MUL: return Token.ASSIGN_MUL; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_DIV: return Token.ASSIGN_DIV; case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_MOD: return Token.ASSIGN_MOD; case com.google.javascript.jscomp.mozilla.rhino.Token.HOOK: return Token.HOOK; case com.google.javascript.jscomp.mozilla.rhino.Token.COLON: return Token.COLON; case com.google.javascript.jscomp.mozilla.rhino.Token.OR: return Token.OR; case com.google.javascript.jscomp.mozilla.rhino.Token.AND: return Token.AND; case com.google.javascript.jscomp.mozilla.rhino.Token.INC: return Token.INC; case com.google.javascript.jscomp.mozilla.rhino.Token.DEC: return Token.DEC; case com.google.javascript.jscomp.mozilla.rhino.Token.DOT: return Token.DOT; case com.google.javascript.jscomp.mozilla.rhino.Token.FUNCTION: return Token.FUNCTION; case com.google.javascript.jscomp.mozilla.rhino.Token.EXPORT: return Token.EXPORT; case com.google.javascript.jscomp.mozilla.rhino.Token.IMPORT: return Token.IMPORT; case com.google.javascript.jscomp.mozilla.rhino.Token.IF: return Token.IF; case com.google.javascript.jscomp.mozilla.rhino.Token.ELSE: return Token.ELSE; case com.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>google.javascript.jscomp.mozilla.rhino.Token.SWITCH: return Token.SWITCH; case com.google.javascript.jscomp.mozilla.rhino.Token.CASE: return Token.CASE; case com.google.javascript.jscomp.mozilla.rhino.Token.DEFAULT: return Token.DEFAULT; case com.google.javascript.jscomp.mozilla.rhino.Token.WHILE: return Token.WHILE; case com.google.javascript.jscomp.mozilla.rhino.Token.DO: return Token.DO; case com.google.javascript.jscomp.mozilla.rhino.Token.FOR: return Token.FOR; case com.google.javascript.jscomp.mozilla.rhino.Token.BREAK: return Token.BREAK; case com.google.javascript.jscomp.mozilla.rhino.Token.CONTINUE: return Token.CONTINUE; case com.google.javascript.jscomp.mozilla.rhino.Token.VAR: return Token.VAR; case com.google.javascript.jscomp.mozilla.rhino.Token.WITH: return Token.WITH; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH: return Token.CATCH; case com.google.javascript.jscomp.mozilla.rhino.Token.FINALLY: return Token.FINALLY; case com.google.javascript.jscomp.mozilla.rhino.Token.VOID: return Token.VOID; case com.google.javascript.jscomp.mozilla.rhino.Token.RESERVED: return Token.RESERVED; case com.google.javascript.jscomp.mozilla.rhino.Token.EMPTY: return Token.EMPTY; case com.google.javascript.jscomp.mozilla.rhino.Token.BLOCK: return Token.BLOCK; case com.google.javascript.jscomp.mozilla.rhino.Token.LABEL: return Token.LABEL; case com.google.javascript.jscomp.mozilla.rhino.Token.TARGET: return Token.TARGET; case com.google.javascript.jscomp.mozilla.rhino.Token.LOOP: return Token.LOOP; case com.google.javascript.jscomp.mozilla.rhino

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.Token.EXPR_VOID: case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_RESULT: return Token.EXPR_RESULT; case com.google.javascript.jscomp.mozilla.rhino.Token.JSR: return Token.JSR; case com.google.javascript.jscomp.mozilla.rhino.Token.SCRIPT: return Token.SCRIPT; case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOFNAME: return Token.TYPEOFNAME; case com.google.javascript.jscomp.mozilla.rhino.Token.USE_STACK: return Token.USE_STACK; case com.google.javascript.jscomp.mozilla.rhino.Token.SETPROP_OP: return Token.SETPROP_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.SETELEM_OP: return Token.SETELEM_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.LOCAL_BLOCK: return Token.LOCAL_BLOCK; case com.google.javascript.jscomp.mozilla.rhino.Token.SET_REF_OP: return Token.SET_REF_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.DOTDOT: return Token.DOTDOT; case com.google.javascript.jscomp.mozilla.rhino.Token.COLONCOLON: return Token.COLONCOLON; case com.google.javascript.jscomp.mozilla.rhino.Token.XML: return Token.XML; case com.google.javascript.jscomp.mozilla.rhino.Token.DOTQUERY: return Token.DOTQUERY; case com.google.javascript.jscomp.mozilla.rhino.Token.XMLATTR: return Token.XMLATTR; case com.google.javascript.jscomp.mozilla.rhino.Token.XMLEND: return Token.XMLEND; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_OBJECT: return Token.TO_OBJECT; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_DOUBLE: return Token.TO_

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>DOUBLE; case com.google.javascript.jscomp.mozilla.rhino.Token.GET: return Token.GET; case com.google.javascript.jscomp.mozilla.rhino.Token.SET: return Token.SET; case com.google.javascript.jscomp.mozilla.rhino.Token.CONST: return Token.CONST; case com.google.javascript.jscomp.mozilla.rhino.Token.SETCONST: return Token.SETCONST; case com.google.javascript.jscomp.mozilla.rhino.Token.DEBUGGER: return Token.DEBUGGER; } // Token without name throw new IllegalStateException(String.valueOf(token)); } /** * A simple wrapper struct, so that we can put a Node in the collection * first and populate it later. */ private final class NodeWithJsDoc { private Node node = null; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> return getPassConfig().getScopeCreator(); } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main js sources. jsRoot = new Node(Token.BLOCK); jsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer == TracerMode.ALL); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer("parseInputs"); try { // Parse externs sources. externsRoot = new Node(Token.BLOCK); externsRoot.setIsSyntheticBlock(true); for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Check if the sources need to be re-ordered. if (options.manageClosureDependencies) { for (CompilerInput input : inputs) { input.setCompiler

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> sia = new SourceInformationAnnotator(input.getName()); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } externAndJsRoot = new Node(Token.BLOCK, externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); return externAndJsRoot; } finally { stopTracer(tracer, "parseInputs"); } } public Node parse(JSSourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [synthetic] ", js)); inputsByName.put(input.getName(), input); return input.getAstRoot(this); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(new CompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(JSSourceFile.fromCode(fileName, js)); } Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsByName == null) { inputsByName = Maps.newHashMap(); } inputsByName.put(input.getName(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to js code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode =

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Inputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out js code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.getType() == Token.SCRIPT); String delimiter = options.inputDelimiter; String sourceName = (String)root.getProp(Node.SOURCENAME_PROP); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter.replaceAll("%name%", sourceName) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> { try { parse(compiler, sourceFile.getName(), sourceFile.getCode()); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } } private void parse(AbstractCompiler compiler, String sourceName, String sourceStr) { try { logger_.fine("Parsing: " + sourceName); root = ParserRunner.parse(sourceName, sourceStr, compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); } catch (IOException e) { compiler.report(JSError.make(AbstractCompiler.READ_ERROR, sourceName)); } if (root == null || compiler.hasHaltingErrors()) { // There was a parse error or IOException, so use a dummy block. root = new Node(Token.BLOCK); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.putProp(Node.SOURCENAME_PROP, sourceName); } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Group ...groups) { Set<DiagnosticType> set = Sets.newHashSet(); for (DiagnosticGroup group : groups) { set.addAll(group.types); } this.types = ImmutableSet.copyOf(set); } /** * Returns whether the given error's type matches a type * in this group. */ public boolean matches(JSError error) { return matches(error.getType()); } /** * Returns whether the given type matches a type in this group. */ public boolean matches(DiagnosticType type) { return types.contains(type); } /** * Returns whether all of the types in the given group are in this group. */ boolean isSubGroup(DiagnosticGroup group) { for (DiagnosticType type : group.types) { if (!matches(type)) { return false; } } return true; } /** * Returns an iterator over all the types in this group. */ Collection<DiagnosticType> getTypes() { return types; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> order // makes more sense. Now, resolution via registry is first in order to // avoid triggering the warnings built into the resolution via properties. boolean resolved = resolveViaRegistry(t, enclosing); if (detectImplicitPrototypeCycle()) { handleTypeCycle(t); } if (resolved) { super.resolveInternal(t, enclosing); return referencedType; } resolveViaProperties(t, enclosing); if (detectImplicitPrototypeCycle()) { handleTypeCycle(t); } super.resolveInternal(t, enclosing); return referencedType; } /** * Resolves a named type by looking it up in the registry. * @return True if we resolved successfully. */ private boolean resolveViaRegistry( ErrorReporter t, StaticScope<JSType> enclosing) { ObjectType type = ObjectType.cast(registry.getType(reference)); if (type != null) { setReferencedType(type, t, enclosing); return true; } return false; } /** * Resolves a named type by looking up its first component in the scope, and * subsequent components as properties. The scope must have been fully * parsed and a symbol table constructed. */ private void resolveViaProperties(ErrorReporter t, StaticScope<JSType> enclosing) { String[] componentNames = reference.split("\\.", -1); if (componentNames[0].length() == 0) { handleUnresolvedType(t); return; } StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]); if (slot == null) { handleUnresolvedType(t); return; } // If the first component has a type of 'Unknown', then any type // names using it should be regarded as silently 'Unknown' rather than be // noisy about it. JSType slotType = slot.getType(); if (slotType == null || slotType.isAllType() || slotType.isNoType()) { handleUnresolvedType(t); return; } JSType value = getTypedefType(t, slot, componentNames[0]); if (value == null) { handleUnresolvedType(t); return; } // resolving component by component

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>defType(ErrorReporter t, StaticSlot<JSType> slot, String name) { JSType type = slot.getType(); if (type != null) { return type; } handleUnresolvedType(t); return null; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } public Scope createScope(Node n, Scope parent) { sourceName = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext().getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name);

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = false; JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); if (!allowDupe) { compiler.report( JSError.make(sourceName, nodeWithLineNumber, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(sourceName, nodeWithLineNumber, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param name The variable name * @param n The node corresponding to the variable name (usually a NAME node) * @param parent The parent node of {@code n} * @param gramps The parent node of {@code parent} * @param declaredType The variable's type, according to JSDoc * @param nodeWithLineNumber The node to use to access the line number of * the variable declaration, if needed */ private void declareVar(String name, Node n, Node parent, Node gramps, JSType declaredType, Node nodeWithLineNumber) { if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, parent, gramps, nodeWithLineNumber); } else { scope.declare(name, n, declaredType, compiler.getInput(sourceName)); } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import java.nio.charset.Charset; import java.util.Set; /** * A code generator that outputs type annotations for functions and * constructors. * */ class TypedCodeGenerator extends CodeGenerator { TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) { super(consumer, outputCharset); } @Override void add(Node n, Context context) { Node parent = n.getParent(); if (parent != null && (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT)) { if (n.getType() == Token.FUNCTION) { add(getFunctionAnnotation(n)); } else if (n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN) { Node rhs = n.getFirstChild().getLastChild(); add(getTypeAnnotation(rhs)); } else if (n.getType() == Token.VAR && n.getFirstChild().getFirstChild() != null && n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) { add(getFunctionAnnotation(n.getFirstChild().getFirstChild

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>())); } } super.add(n, context); } private String getTypeAnnotation(Node node) { JSType type = node.getJSType(); if (type instanceof FunctionType) { return getFunctionAnnotation(node); } else if (type != null && !type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.getType() == Token.FUNCTION); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = (FunctionType) fnNode.getJSType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * @param {" + n.getJSType() + "} "); sb.append(paramNode.getString()); sb.append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * @return {" + retType + "}\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { FunctionType superConstructor = funType.getSuperClassConstructor(); if (super

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> { this.rootRenamer = new ContextualRenamer(); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used the starting context can not // be a function. Preconditions.checkState( declarationRoot.getType() != Token.FUNCTION || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (declarationRoot.getType() == Token.FUNCTION) { // Add the function parameters Node fnParams = declarationRoot.getFirstChild().getNext(); for (Node c = fnParams.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = declarationRoot.getLastChild(); findDeclaredNames(functionBody, null, renamer); } else { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null && !NodeUtil.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>isFunctionDeclaration(n)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove function recursive name (if any). nameStack.pop(); break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> /** * Prepare a set for the new scope. */ public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } referenceStack.push(referencedNames); referencedNames = Sets.newHashSet(); } /** * Rename vars for the current scope, and merge any referenced * names into the parent scope reference set. */ public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); handleScopeVar(v); } // Merge any names that were referenced but not declared in the current // scope. Set<String> current = referencedNames; referencedNames = referenceStack.pop(); // If there isn't anything left in the stack we will be going into the // global scope: don't try to build a set of referenced names for the // global scope. if (!referenceStack.isEmpty()) { referencedNames.addAll(current); } } /** * For the Var declared in the current scope determine if it is possible * to revert the name to its orginal form without conflicting with other * values. */ void handleScopeVar(Var v) { String name = v.getName(); if (containsSeparator(name)) { String newName = getOrginalName(name); // Check if the new name is valid and if it would cause conflicts. if (TokenStream.isJSIdentifier(newName) && !referencedNames.contains(newName) && !newName.equals(ARGUMENTS)) { referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.getType() == Token.NAME); n.setString(newName); } compiler.reportCodeChange(); } nameMap.remove(name); } } @Override public

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> private JSType currentClass = null; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); } public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.getType() == Token.ASSIGN) { Node lValue = parent.getFirstChild(); if (lValue.isQualifiedName()) { if (lValue.getType() == Token.GETPROP) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return ((FunctionType) lValueType).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> } else if (NodeUtil.isFunctionDeclaration(n) || parent.getType() == Token.NAME) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isConstructor()) { return ((FunctionType) type).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDe

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>precation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR || parent.getType() == Token.NEW) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.getType() == Token.NEW) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) {

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE && !t.getInput().getName().equals(docInfo.getSourceName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), docInfo.getSourceName())); } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = t.inGlobalScope() && parent.getType() == Token.ASSIGN && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } boolean sameInput = t.getInput().getName().equals(docInfo.getSourceName()); Visibility visibility = docInfo.getVisibility(); JSType ownerType = normalizeClassType(objectType); if (isOverride)

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> example, * new PrivateCtor_(); // not legal * PrivateCtor_.newInstance(); // legal * x instanceof PrivateCtor_ // legal * * This is a weird special case, because our visibility system is inherited * from Java, and JavaScript has no distinction between classes and * constructors like Java does. * * We may want to revisit this if we decide to make the restrictions tighter. */ private static boolean isValidPrivateConstructorAccess(Node parent) { return parent.getType() != Token.NEW; } /** * Determines whether a deprecation warning should be emitted. * @param t The current traversal. * @param n The node which we are checking. * @param parent The parent of the node which we are checking. */ private boolean shouldEmitDeprecationWarning( NodeTraversal t, Node n, Node parent) { // In the global scope, there are only two kinds of accesses that should // be flagged for warnings: // 1) Calls of deprecated functions and methods. // 2) Instantiations of deprecated classes. // For now, we just let everything else by. if (t.inGlobalScope()) { if (!((parent.getType() == Token.CALL && parent.getFirstChild() == n) || n.getType() == Token.NEW)) { return false; } } // We can always assign to a deprecated property, to keep it up to date. if (n.getType() == Token.GETPROP && n == parent.getFirstChild() && NodeUtil.isAssignmentOp(parent)) { return false; } return !canAccessDeprecatedTypes(t); } /** * Returns whether it's currently ok to access deprecated names and * properties. * * There are 3 exceptions when we're allowed to use a deprecated * type or property: * 1) When we're in a deprecated function. * 2) When we're in a deprecated class. * 3) When we're in a static method of a deprecated class. */ private boolean canAccessDeprecatedTypes(NodeTraversal t) { Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>2 (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || // Case #3 (scopeRootParent != null && scopeRootParent.getType() == Token.ASSIGN && getTypeDeprecationInfo( getClassOfMethod(scopeRoot, scopeRootParent)) != null); } /** * Returns whether this is a function node annotated as deprecated. */ private static boolean isDeprecatedFunction(Node n, Node parent) { if (n.getType() == Token.FUNCTION) { JSType type = n.getJSType(); if (type != null) { return getTypeDeprecationInfo(type) != null; } } return false; } /** * Returns the deprecation reason for the type if it is marked * as being deprecated. Returns empty string if the type is deprecated * but no reason was given. Returns null if the type is not deprecated. */ private static String getTypeDeprecationInfo(JSType type) { if (type == null) { return null; } JSDocInfo info = type.getJSDocInfo(); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType objType = ObjectType.cast(type); if (objType != null) { ObjectType implicitProto = objType.getImplicitPrototype(); if (implicitProto != null) { return getTypeDeprecationInfo(implicitProto); } } return null; } /** * Returns the deprecation reason for the property if it is marked * as being deprecated. Returns empty string if the property is deprecated * but no reason was given. Returns null if the property is not deprecated. */ private static String getPropertyDeprecationInfo(ObjectType type, String prop) { JSDocInfo info = type.getOwnPropertyJSDocInfo(prop); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType implicitProto = type.getImplicitPrototype(); if (implicitProto != null) { return getPropertyDeprecationInfo(implicitProto, prop); }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> loops. */ private static PassFactory createEmptyPass(String name) { return new PassFactory(name, true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return runInSerial(); } }; } /** * Runs custom passes that are designated to run at a particular time. */ private PassFactory getCustomPasses( final CustomPassExecutionTime executionTime) { return new PassFactory("runCustomPasses", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return runInSerial(options.customPasses.get(executionTime)); } }; } /** * All inlining is forbidden in heuristic renaming mode, because inlining * will ruin the invariants that it depends on. */ private boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial(final CompilerPass ... passes) { return runInSerial(Lists.newArrayList(passes)); } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial( final Collection<CompilerPass> passes) { return new CompilerPass() { @Override public void process(Node externs, Node root) { for (CompilerPass pass : passes) { pass.process(externs, root); } } }; } @VisibleForTesting static Map<String, Node> getAdditionalReplacements( CompilerOptions options) { Map<String, Node> additionalReplacements = Maps.newHashMap(); if (options.markAsCompiled || options.closurePass) { additionalReplacements.put(COMPILED_CONSTANT_NAME, new Node(Token.TRUE)); } if (options.closurePass && options.locale != null) { additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, Node.newString(options.locale)); } return additionalReplacements; } /** A compiler pass that marks pure functions. */ private static class PureFunctionMarker implements CompilerPass

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * A builder for the Rhino Node representing Function parameters. * @author nicksantos@google.com (Nick Santos) */ public class FunctionParamBuilder { private final JSTypeRegistry registry; private final Node root = new Node(Token.LP); public

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> FunctionParamBuilder(JSTypeRegistry registry) { this.registry = registry; } /** * Add parameters of the given type to the end of the param list. * @return False if this is called after optional params are added. */ public boolean addRequiredParams(JSType ...types) { if (hasOptionalOrVarArgs()) { return false; } for (JSType type : types) { newParameter(type); } return true; } /** * Add optional parameters of the given type to the end of the param list. * @param types Types for each optional parameter. The builder will make them * undefineable. * @return False if this is called after var args are added. */ public boolean addOptionalParams(JSType ...types) { if (hasVarArgs()) { return false; } for (JSType type : types) { newParameter(registry.createOptionalType(type)).setOptionalArg(true); } return true; } /** * Add variable arguments to the end of the parameter list. * @return False if this is called after var args are added. */ public boolean addVarArgs(JSType type) { if (hasVarArgs()) { return false; } // There are two types of variable argument functions: // 1) Programmer-defined var args // 2) Native bottom types that can accept any argument. // For the first one, "undefined" is a valid value for all arguments. // For the second, we do not want to cast it up to undefined. if (!type.isEmptyType()) { type = registry.createOptionalType(type); } newParameter(type).setVarArgs(true); return true; } /** * Copies the parameter specification from the given node. */ public void newParameterFromNode(Node n) { Node newParam = newParameter(n.getJSType()); newParam.setVarArgs(n.isVarArgs()); newParam.setOptionalArg(n.isOptionalArg()); } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, "");

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> for this slot. * @return The type or {@code null} if no type is declared for it. */ T getType(); /** * Returns whether the type has been inferred (as opposed to declared). */ boolean isTypeInferred(); }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> this to generate deps files. (We're only using it for // symbol dependencies.) DependencyInfo info = (new JsFileParser(errorManager)).parseFile( getName(), getName(), getCode()); provides.addAll(info.getProvides()); requires.addAll(info.getRequires()); generatedDependencyInfoFromSource = true; } } } private static class DepsFinder { private final List<String> provides = Lists.newArrayList(); private final List<String> requires = Lists.newArrayList(); private final CodingConvention codingConvention = new ClosureCodingConvention(); void visitTree(Node n) { visitSubtree(n, null); } void visitSubtree(Node n, Node parent) { if (n.getType() == Token.CALL) { String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent); if (provide != null) { provides.add(provide); } return; } else if (parent != null && parent.getType() != Token.EXPR_RESULT && parent.getType() != Token.SCRIPT) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> compiler. * * This is a preferred internal constructor. */ private JSError(String sourceName, int lineno, int charno, DiagnosticType type, CheckLevel level, String... arguments) { this.type = type; this.description = type.format.format(arguments); this.lineNumber = lineno; this.charno = charno; this.sourceName = sourceName; this.level = level == null ? type.level : level; } /** * Creates a JSError for a source file location. Package private to avoid * any entanglement with code outside of the compiler. * * This is a preferred internal constructor. */ private JSError(String sourceName, Node node, DiagnosticType type, String... arguments) { this(sourceName, (node != null) ? node.getLineno() : -1, (node != null) ? node.getCharno() : -1, type, null, arguments); } public DiagnosticType getType() { return type; } /** * Format a message at the given level. * * @return the formatted message or {@code null} */ public String format(CheckLevel level, MessageFormatter formatter) { switch (level) { case ERROR: return formatter.formatError(this); case WARNING: return formatter.formatWarning(this); default: return null; } } @Override public String toString() { // TODO(user): remove custom toString. return type.key + ". " + description + " at " + (sourceName != null && sourceName.length() > 0 ? sourceName : "(unknown source)") + " line " + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") + " : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)"); } /** * Get the character number. */ public int getCharno() { return charno; } @Override public boolean equals(Object o) { // Generated by Intellij IDEA if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> abstract T processSwitchCase(SwitchCase caseNode); abstract T processSwitchStatement(SwitchStatement statementNode); abstract T processThrowStatement(ThrowStatement statementNode); abstract T processTryStatement(TryStatement statementNode); abstract T processUnaryExpression(UnaryExpression exprNode); abstract T processVariableDeclaration(VariableDeclaration declarationNode); abstract T processVariableInitializer(VariableInitializer initializerNode); abstract T processWhileLoop(WhileLoop loopNode); abstract T processWithStatement(WithStatement statementNode); abstract T processIllegalToken(AstNode node); public T process(AstNode node) { switch (node.getType()) { case Token.ADD: case Token.AND: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.OR: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return processInfixExpression((InfixExpression) node); case Token.ARRAYLIT: return processArrayLiteral((ArrayLiteral) node); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_DIV: case Token.ASSIGN_LSH: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_RSH: case Token.ASSIGN_SUB: case Token.ASSIGN_URSH: return processAssignment((Assignment) node); case Token.BITNOT: case Token.DEC: case Token.DELPROP: case Token.INC: case Token.NEG: case Token.NOT: case Token.POS: case Token.TYPEOF: case Token.VOID: return processUnaryExpression((UnaryExpression) node); case Token.BLOCK: if (node instanceof Block

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>) { return processBlock((Block) node); } else if (node instanceof Scope) { return processScope((Scope) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.BREAK: return processBreakStatement((BreakStatement) node); case Token.CALL: return processFunctionCall((FunctionCall) node); case Token.CASE: case Token.DEFAULT: return processSwitchCase((SwitchCase) node); case Token.CATCH: case Token.FINALLY: return processCatchClause((CatchClause) node); case Token.COLON: return processObjectProperty((ObjectProperty) node); case Token.CONTINUE: return processContinueStatement((ContinueStatement) node); case Token.DO: return processDoLoop((DoLoop) node); case Token.EMPTY: return processEmptyExpression((EmptyExpression) node); case Token.EXPR_RESULT: case Token.EXPR_VOID: if (node instanceof ExpressionStatement) { return processExpressionStatement((ExpressionStatement) node); } else if (node instanceof LabeledStatement) { return processLabeledStatement((LabeledStatement) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.DEBUGGER: case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: return processKeywordLiteral((KeywordLiteral) node); case Token.FOR: if (node instanceof ForInLoop) { return processForInLoop((ForInLoop) node); } else if (node instanceof ForLoop) { return processForLoop((ForLoop) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.FUNCTION: return processFunctionNode((FunctionNode) node); case Token.GETELEM: return processElementGet((ElementGet) node); case Token.GETPROP: return processPropertyGet

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariableInitializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> emulates the <code>Boolean()</code> JavaScript cast function. * * @throws IllegalArgumentException If {@code n} is not a literal value */ static boolean getBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return n.getString().length() > 0; case Token.NUMBER: return n.getDouble() != 0; case Token.NULL: case Token.FALSE: case Token.VOID: return false; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return false; } else if ("Infinity".equals(name)) { return true; } break; case Token.TRUE: case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: return true; } throw new IllegalArgumentException("Non-literal value: " + n); } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): Convert constant array, object, and regex literals as well. switch (n.getType()) { case Token.NAME: case Token.STRING: return n.getString(); case Token.NUMBER: double value = n.getDouble(); long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(n.getDouble()); } case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; } return null; } /** * Gets the function's name. This method recognizes five forms: * <ul> * <li>{@code function name() ...}</li> * <li>{@code var

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: case Token.VOID: return true; case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * function() { return a; } is not. */ static boolean isLiteralValue(Node

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> n) { // TODO(nicksantos): Refine this function to catch more literals. switch (n.getType()) { case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child)) { return false; } } return true; default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Single operators are valid if the child is valid. case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.NOT: case Token.NEG: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (block.getType() != Token.BLOCK) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (n.getType() != Token.EMPTY) { return false; } } return true; } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GETELEM: case Token.GETPROP: case Token.GT: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.NOT: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.TYPEOF: case Token.VOID: case Token.POS: case Token.NEG: case Token.URSH: return true; default: return false; } } /** * Creates an EXPR_RESULT. * * @param child The expression itself. * @return Newly created EXPR node with the child as subexpression. */ public static Node newExpr(Node child) { Node expr = new Node(Token.EXPR_RESULT, child) .copyInformationFrom(child); return expr; } /** * Returns true if the node may create new mutable state, or change existing * state. * * @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a> */ static boolean mayEffectMutableState(Node n) { return mayEffectMutableState(n, null); } static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, true, compiler); } /** * Returns true if the node which may have side effects when executed. */ static boolean mayHaveSideEffects(Node n) { return mayHaveSideEffects(n, null); } static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, false, compiler); } /** * Returns true if some node in n's subtree changes application state. * If {@code checkForNewObjects} is true, we assume that newly created * mutable objects (like object literals)

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> change state. Otherwise, we assume * that they have no side effects. */ private static boolean checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.LP: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) return true; break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) break; if (isAssignmentOp(n)) { // Assignments will have side effects if

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } Node current = n.getFirstChild(); for (; current.getType() == Token.GETPROP || current.getType() == Token.GETELEM; current = current.getFirstChild()) { } return !(isLiteralValue(current) || current.getType() == Token.FUNCTION); } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.NEW, "Expected NEW node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.getType() == Token.NAME && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects( Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { Preconditions.checkArgument( callNode.getType() == Token.CALL, "Expected CALL node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.getType() == Token.NAME) { String name = nameNode.getString(); if (name.equals("String")) { return false; } } else if (nameNode.getType() == Token.GETPROP) { // Functions in the "Math" namespace have no side effects. if (nameNode.getFirstChild().getType() == Token.NAME) { String namespaceName = nameNode.getFirstChild().getString(); if (namespaceName.equals("Math")) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().getType() == Token.REGEXP && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().getType() == Token.STRING && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.getType() == Token.STRING || param.getType() == Token.REGEXP)) return false; } } } return true; } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.CALL: case Token.DELPROP

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>: case Token.NEW: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.ARRAYLIT: case Token.CALL: case Token.EMPTY

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>: case Token.FALSE: case Token.FUNCTION: case Token.GETELEM: case Token.GETPROP: case Token.GET_REF: case Token.IF: case Token.LP: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.RETURN: case Token.STRING: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Node.tokenToName(type) + " (type " + type + ")"); } } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatentation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assiment op"); } static boolean isExpressionNode(Node n) { return n.getType() == Token.EXPR_RESULT; } /** * Determines if the given node contains a function statement or function * expression. */ static boolean containsFunction(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the shallow scope contains references to 'this' keyword */ static boolean referencesThis(Node n) { return containsType(n, Token.THIS, new MatchNotFunction()); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.getType() == Token.GETPROP || n.getType() == Token.GETELEM; } /** * Is this a GETPROP node? */ static boolean isGetProp(Node n) { return n.getType() == Token.GETPROP; } /** * Is this a NAME node? */ static boolean isName(Node n) { return n.getType() == Token.NAME; } /** * Is this a NEW node? */ static boolean isNew(Node n) { return n.getType() == Token.NEW; } /** * Is this a VAR node? */ static boolean isVar(Node n) { return n.getType() == Token.VAR; } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree.

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> return n.getType() == Token.NAME && n.getParent().getType() == Token.VAR; } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(isName(n)); Node parent = n.getParent(); if (isVar(parent)) { return n.getFirstChild(); } else if (isAssign(parent) && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this a STRING node? */ static boolean isString(Node n) { return n.getType() == Token.STRING; } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN; } /** * Is this an ASSIGN node? */ static boolean isAssign(Node n) { return n.getType() == Token.ASSIGN; } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.CALL; } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.getType() == Token.FOR && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default:

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild(); case Token.DO: return n.getFirstChild(); default: return null; } } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.getType() == Token.CASE || n.getType() == Token.DEFAULT; } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return isName(n) && !n.getString().isEmpty(); } /** @return Whether the node is a label name. */ static boolean isLabelName(Node n) { return (n != null && n.getType() == Token.LABEL_NAME); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.getType() == Token.TRY && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { // Node parent = node.getParent(); if (isStatementBlock(parent) || isSwitchCase(node) || isTryFinallyNode(parent, node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (node.getType() == Token.BLOCK) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (parent.getType() == Token.LABEL && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.getType() == Token.FOR && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, new Node(Token.EMPTY)); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.getType() == Token.BLOCK); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else if (parent.getType() == Token.LABEL && block.hasOneChild()) { parent.replaceChild(block, block.removeFirstChild()); return true; } else { return false; } } /** * Is this a CALL node? */ static boolean isCall(Node n) { return n.getType() == Token.CALL; } /** * Is this a FUNCTION node? */ static boolean isFunction(Node n) { return n.getType() == Token.FUNCTION; } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(isFunction(fn)); return fn.getLastChild(); } /** * Is this a THIS node? */ static boolean isThis(Node node) { return node.getType() == Token.THIS; } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.getType() == Token.FUNCTION && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().getType() == Token.SCRIPT || n.getParent().getParent().getType() == Token.FUNCTION); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> is not added to the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} } * </pre> * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.getType() == Token.FUNCTION && !isStatement(n); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(isFunction(function)); return isNameReferenced( function.getLastChild(), "arguments", new MatchNotFunction()); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.getType() == Token.CALL) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.getType() == Token.STRING) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true; } } return false; } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isLhs(Node n, Node parent) { return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) || parent.getType() == Token.VAR; } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { if (node.getType() == Token.STRING && parent.getType() == Token.OBJECTLIT) { int index = 0; for (Node current = parent.getFirstChild(); current != null; current = current.getNext()) { if (current == node) { return index % 2 == 0; } index++; } } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|";

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> case Token.OR: return "||"; case Token.BITXOR: return "^"; case Token.AND: return "&&"; case Token.BITAND: return "&"; case Token.SHEQ: return "==="; case Token.EQ: return "=="; case Token.NOT: return "!"; case Token.NE: return "!="; case Token.SHNE: return "!=="; case Token.LSH: return "<<"; case Token.IN: return "in"; case Token.LE: return "<="; case Token.LT: return "<"; case Token.URSH: return ">>>"; case Token.RSH: return ">>"; case Token.GE: return ">="; case Token.GT: return ">"; case Token.MUL: return "*"; case Token.DIV: return "/"; case Token.MOD: return "%"; case Token.BITNOT: return "~"; case Token.ADD: return "+"; case Token.SUB: return "-"; case Token.POS: return "+"; case Token.NEG: return "-"; case Token.ASSIGN: return "="; case Token.ASSIGN_BITOR: return "|="; case Token.ASSIGN_BITXOR: return "^="; case Token.ASSIGN_BITAND: return "&="; case Token.ASSIGN_LSH: return "<<="; case Token.ASSIGN_RSH: return ">>="; case Token.ASSIGN_URSH: return ">>>="; case Token.ASSIGN_ADD: return "+="; case Token.ASSIGN_SUB: return "-="; case Token.ASSIGN_MUL: return "*="; case Token.ASSIGN_DIV: return "/="; case Token.ASSIGN_MOD: return "%="; case Token.VOID: return "void"; case Token.TYPEOF: return "typeof"; case Token.INSTANCEOF: return "instanceof"; default: return null; } } /** * Converts an operator's token value (see {@link Token}) to a string * representation or fails. * * @param operator the operator's token value to convert * @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString()) .copyInformationFrom(nameNode)) .copyInformationFrom(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.getType() == Token.BLOCK || addingRoot.getType() == Token.SCRIPT); Preconditions.checkState(addingRoot.getFirstChild() == null || addingRoot.getFirstChild().getType() != Token.SCRIPT); return addingRoot; } /** Creates function name(params_0, ..., params_n) { body }. */ public static Node newFunctionNode(String name, List<Node> params, Node body, int lineno, int charno) { Node parameterParen = new Node(Token.LP, lineno, charno); for (Node param : params) { parameterParen.addChildToBack(param); } Node function = new Node(Token.FUNCTION, lineno, charno); function.addChildrenToBack( Node.newString(Token.NAME, name, lineno, charno)); function.addChildToBack(parameterParen); function.addChildToBack(body); return function; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param lineno The source line offset. * @param charno The source character offset from start of the line. * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode(String name, int lineno, int charno) { int endPos = name.indexOf('.'); if (endPos == -1) { return Node.newString(Token.NAME, name, lineno, charno); } Node node = Node.newString(Token.NAME, name.substring(0, endPos), lineno, charno); int startPos; do { startPos = endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); node = new Node(Token.GETPROP, node, Node.newString(Token.STRING,

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> part, lineno, charno), lineno, charno); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode(String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(name, -1, -1); setDebugInformation(node, basisNode, originalName); return node; } /** * Sets the debug information (source file info and orignal name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copyInformationFromForTree(basisNode); node.putProp(Node.ORIGINALNAME_PROP, originalName); } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * * @return The node created. */ static Node newName(String name, Node basisNode) { Node nameNode = Node.newString(Token.NAME, name); nameNode.copyInformationFrom(basisNode); return nameNode; } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name The name for the

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Collector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); public void visit(Node n) { if (n.getType() == Token.NAME) { Node parent = n.getParent(); if (parent != null && parent.getType() == Token.VAR) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, new MatchNotFunction()); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Node newUndefinedNode(Node srcReferenceNode) { // TODO(johnlenz): Why this instead of the more common "undefined"? Node node = new Node(Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { public boolean apply(Node n) { return isFunctionDeclaration(n) || n.getType() == Token.VAR; } } /** * A predicate for matching anything except function nodes. */ static class MatchNotFunction implements Predicate<Node>{ public boolean apply(Node n) { return !isFunction(n); } } /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ public boolean apply(Node n) { Node parent = n.getParent(); return n.getType() == Token.BLOCK || (!

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>isFunction(n) && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node> traverseChildrenPred) { return getCount(node, new MatchNodeType(type), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNameNode(name), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name) { return isNameReferenced(node, name, Predicates.<Node>alwaysTrue()); } /** * Finds the number of times a simple name is referenced within the node tree. */ static int getNameReferenceCount(Node node, String name) { return getCount( node, new MatchNameNode(name), Predicates.<Node>alwaysTrue()); } /** * @return Whether the predicate is true for the node or any of its children. */ static boolean has(Node node, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { if (pred.apply(node)) { return true; } if (!traverseChildrenPred.apply(node)) { return false; } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (has(c, pred, traverseChildrenPred)) { return true; } } return false; } /** * @return The number of times the the predicate is true for the node * or any of its children. */ static int getCount( Node n, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { int total = 0; if (pred.apply(n)) { total++; } if (traverseChildrenPred.apply(n)) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { total += getCount(

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>c, pred, traverseChildrenPred); } } return total; } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { vistor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, vistor, traverseChildrenPred); } } } /** * A post-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, vistor, traverseChildrenPred); } } vistor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.getType() == Token.BLOCK); return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ static Node getFnParameters(Node fn

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Node) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** * @param nameNode A name node * @return The JSDocInfo for the name node */ static JSDocInfo getInfoForNameNode(Node nameNode) { JSDocInfo info = null; Node parent = null; if (nameNode != null) { info = nameNode.getJSDocInfo(); parent = nameNode.getParent(); } if (info == null && parent != null && ((parent.getType() == Token.VAR && parent.hasOneChild()) || parent.getType() == Token.FUNCTION)) { info = parent.getJSDocInfo(); } return info; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); n = n.getParent(); } return sourceName; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>, null); } void add(String str) { cc.add(str); } private void addIdentifier(String identifier) { cc.addIdentifier(identifierEscape(identifier)); } void add(Node n) { add(n, Context.OTHER); } void add(Node n, Context context) { if (!cc.continueProcessing()) { return; } int type = n.getType(); String opstr = NodeUtil.opToStr(type); int childCount = n.getChildCount(); Node first = n.getFirstChild(); Node last = n.getLastChild(); // Handle all binary operators if (opstr != null && first != last) { Preconditions.checkState(childCount == 2); int p = NodeUtil.precedence(type); addLeftExpr(first, p, context); cc.addOp(opstr, true); // For right-hand-side of operations, only pass context if it's // the IN_FOR_INIT_CLAUSE one. Context rhsContext = getContextForNoInOperator(context); // Handle associativity. // e.g. if the parse tree is a * (b * c), // we can simply generate a * b * c. if (last.getType() == type && NodeUtil.isAssociative(type)) { addExpr(last, p, rhsContext); } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { // Assignments are the only right-associative binary operators addExpr(last, p, rhsContext); } else { addExpr(last, p + 1, rhsContext); } return; } cc.startSourceMapping(n); switch (type) { case Token.TRY: { Preconditions.checkState(first.getNext().getType() == Token.BLOCK && first.getNext().getChildCount() <= 1); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild();

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 3); if (first.getNext().getType() != Token.EMPTY) { throw new Error("Catch conditions not suppored because I think" + " that it may be a netscape only feature."); } add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.getType() == Token.EMPTY) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.getType() == Token.COMMA) { addExpr(first, NodeUtil.precedence(Token.ASSIGN)); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP)); add("]"); break; case Token

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.LP: add("("); addList(first); add(")"); break; case Token.COMMA: addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), p); cc.addOp(":", true); addExpr(last, p); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.GET_REF: add(first); break; case Token.REF_SPECIAL: Preconditions.checkState(childCount == 1); add(first); add("."); add((String) n.getProp(Node.NAME_PROP)); break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean stripBlock = n.isSyntheticBlock() || ((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2)); if (!stripBlock) { cc.beginBlock(); } for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.getType() == Token.VAR) { cc.endStatement(); } if (c.getType() == Token.FUNCTION) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top level statements are more homogeneous. if (type == Token.SCRIPT) { cc.notePreferredLineBreak(); } } if (!stripBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.getType() == Token.VAR) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false);

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyExpression(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState(childCount == 2); Preconditions.checkState(last.getType() == Token.STRING); boolean needsParens = (first.getType() == Token.NUMBER); if (needsParens) { add("("); } addLeftExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState(childCount == 2); addLeftExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP, 0); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc/dec. if (postProp != 0) { addLeftExpr(first, NodeUtil.precedence(type), context); cc.addOp(o, false); } else { cc.addOp(o

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>, false); add(first); } break; } case Token.CALL: // If the left hand side of the call is a direct reference to eval, // then it must have a DIRECT_EVAL annotation. If it does not, then // that means it was originally an indirect call to eval, and that // indirectness must be preserved. if (first.getType() == Token.NAME && "eval".equals(first.getString()) && !first.getBooleanProp(Node.DIRECT_EVAL)) { add("(0,eval)"); } else { addLeftExpr(first, NodeUtil.precedence(type), context); } add("("); addList(first.getNext()); add(")"); break; case Token.IF: boolean hasElse = childCount == 3; boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse; if (ambiguousElseClause) { cc.beginBlock(); } add("if("); add(first); add(")"); if (hasElse) { addNonEmptyExpression( first.getNext(), Context.BEFORE_DANGLING_ELSE, false); add("else"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { addNonEmptyExpression(first.getNext(), Context.OTHER, false); Preconditions.checkState(childCount == 2); } if (ambiguousElseClause) { cc.endBlock(); } break; case Token.NULL: case Token.THIS: case Token.FALSE: case Token.TRUE: Preconditions.checkState(childCount == 0); add(Node.tokenToName(type)); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_VOID: case Token.EXPR_RESULT: if (type == Token.EXPR_VOID) { throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT."); } Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parens. Otherwise, when parsed, NEW will bind to the // first viable parens if (NodeUtil.containsCall(first)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING: Preconditions.checkState(childCount == 0); add(jsString(n.getString(), outputCharsetEncoder)); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { Preconditions.checkState(childCount % 2 == 0); boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext().getNext()) { if (c != first) { cc.listSeparator(); } // Object literal property names don't have to be quoted if they are // not JavaScript keywords if (

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>c.getType() == Token.STRING && !TokenStream.isKeyword(c.getString()) && TokenStream.isJSIdentifier(c.getString()) && // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(c.getString())) { add(c.getString()); } else { addExpr(c, 1); } add(":"); addExpr(c.getNext(), 1); } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break; case Token.DEFAULT: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), true); break; // This node is auto generated in anonymous functions and should just get // ignored for our purposes. case Token.SETNAME: break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyExpression( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && n.getType

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>() != Token.BLOCK) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.getType() == Token.BLOCK ) { int count = getNonEmptyChildCount(n); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } else { cc.endStatement(true); } return; } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DOs. Node firstAndOnlyChild = getFirstNonEmptyChild(n); boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); if (alwaysWrapInBlock || firstAndOnlyChild.getType() == Token.FUNCTION || firstAndOnlyChild.getType() == Token.DO) { cc.beginBlock(); add(firstAndOnlyChild, Context.STATEMENT); cc.maybeLineBreak(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); return; } else { // Continue with the only child. nodeToProcess = firstAndOnlyChild; } } } if (nodeToProcess.getType() == Token.EMPTY) { cc.endStatement(true); } else { add(nodeToProcess, context); // VAR doesn't include ';' since it gets used in expressions - so any // VAR in a statement context needs a call to endStatement() here. if (nodeToProcess.getType() == Token.VAR) { cc.endStatement(); } } } /** * Adds a node at the left-hand side of an expression. Unlike * {@link #addExpr(Node,int)}, this preserves information about the context. * * The left side of an expression is special because in the JavaScript * grammar, certain tokens may be parsed differently when they are at * the beginning of a statement. For example, "{}" is parsed as a block, * but

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> "{'x': 'y'}" is parsed as an object literal. */ void addLeftExpr(Node n, int minPrecedence, Context context) { addExpr(n, minPrecedence, context); } void addExpr(Node n, int minPrecedence) { addExpr(n, minPrecedence, Context.OTHER); } private void addExpr(Node n, int minPrecedence, Context context) { if ((NodeUtil.precedence(n.getType()) < minPrecedence) || ((context == Context.IN_FOR_INIT_CLAUSE) && (n.getType() == Token.IN))){ add("("); add(n, clearContextForNoInOperator(context)); add(")"); } else { add(n, context); } } void addList(Node firstInList) { addList(firstInList, true, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument) { addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument, Context lhsContext) { for (Node n = firstInList; n != null; n = n.getNext()) { boolean isFirst = n == firstInList; if (isFirst) { addLeftExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); } else { cc.listSeparator(); addExpr(n, isArrayOrFunctionArgument ? 1 : 0); } } } /** * This function adds a comma-separated list as is specified by an ARRAYLIT * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). * @param skipIndexes If not null, then the array of skipped entries in the * array. */ void addList(Node firstInList, int[] skipIndexes) { int nextSlot = 0; int nextSkipSlot = 0; for (Node n = firstInList; n != null; n

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> than using the CharsetEncoder. if (c > 0x1f && c <= 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some js parsers, // or perhaps mangled by proxies along the way, // so we play it safe and unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser's rules for valid identifier characters are // crazy. if (c > 0x1F && c < 0x7F) { sb.append(c); } else { appendHexJavaScriptRepresentation(sb, c); } } return sb.toString(); } /** Gets the number of children of this node that are non empty. */ private static int getNonEmptyChildCount(Node n) { int i = 0; for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { i++; } } return i; } /** Gets the first non-empty child of the given node. */ private static Node getFirstNonEmptyChild(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { return c; } } return null; } // Information on the current context. Used for disambiguating special cases. // For example, a "{" could indicate the start of an object literal or a // block, depending on the current context. enum Context { STATEMENT, BE

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> getLocalVarIndex() { int num = index; Scope s = scope.getParent(); if (s == null) { throw new IllegalArgumentException("Var is not local"); } while (s.getParent() != null) { num += s.getVarCount(); s = s.getParent(); } return num; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotaed by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); return parent.getType() == Token.FUNCTION ? parent : nameNode.getFirstChild(); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isInferred()}. */ public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ public JSDocInfo getJSDocInfo() { return info; } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * A simple pass to ensure that all AST nodes have line numbers, * an that the line numbers are monotonically increasing. * * @author nicksantos@google.com (Nick Santos) */ class LineNumberCheck implements Callback, CompilerPass { static final DiagnosticType MISSING_LINE_INFO = DiagnosticType.error( "JSC_MISSING_LINE_INFO", "No source location information associated with {0}.\n" + "Most likely a Node has been created with settings the source file " + "and line/column location. Usually this is done using " + "Node.copyInformationFrom and supplying a Node from the source AST."); private final AbstractCompiler compiler; private boolean requiresLineNumbers = false; LineNumberCheck(AbstractCompiler compiler) { this.compiler = compiler; } public void setCheckSubTree(Node root) { requiresLineNumbers = true; NodeTraversal.traverse(compiler, root, this); } public void process(Node externs, Node root) { requiresLineNumbers = false; NodeTraversal.traverse(compiler, root, this); } public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Each JavaScript file is rooted in a script node, so we'll only // have line number information inside the script node. if (n.getType() ==

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Token.SCRIPT) { requiresLineNumbers = true; } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.SCRIPT) { requiresLineNumbers = false; } else if (requiresLineNumbers) { if (n.getLineno() == -1) { // The tree version of the node is really the best diagnostic // info we have to offer here. compiler.report( t.makeError(n, MISSING_LINE_INFO, n.toStringTree())); } } } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>BadModuleReference(name, ref); } } } } } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME, ref.module.getName(), name.declaration.module.getName(), name.fullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.getType() == Token.EXPR_RESULT) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return; } } compiler.report( JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING, name.fullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private static boolean propertyMustBeInitializedByFullName(Name name) { // If an object literal in the global namespace is never aliased, // then all of its properties must be defined using its full qualified // name. This implies that its properties must all be in the global // namespace as well. // // The same is not true for FUNCTION and OTHER types, because their // implicit prototypes have properties that are not captured by the global // namespace. return name.parent != null && name.parent.aliasingGets == 0 && name.parent.type == Name.Type.OBJECTLIT; } }

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> * {@inheritDoc} * * <p>Understands several different inheritance patterns that occur in * Google code (various uses of {@code inherits} and {@code mixin}). */ @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { Node callName = callNode.getFirstChild(); SubclassType type = typeofClassDefiningName(callName); if (type != null) { Node subclass = null; Node superclass = callNode.getLastChild(); // There are six possible syntaxes for a class-defining method: // SubClass.inherits(SuperClass) // goog.inherits(SubClass, SuperClass) // goog$inherits(SubClass, SuperClass) // SubClass.mixin(SuperClass.prototype) // goog.mixin(SubClass.prototype, SuperClass.prototype) // goog$mixin(SubClass.prototype, SuperClass.prototype) if (callNode.getChildCount() == 2 && callName.getType() == Token.GETPROP) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { // make sure to strip the prototype off of the nodes // to normalize for goog.mixin return new SubclassRelationship( type, stripPrototype(subclass), stripPrototype(superclass)); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.getType() == Token.GETPROP) { methodName = callName.getLastChild().getString(); } else if (callName.getType() == Token.NAME) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, strip "prototype" off the end. * * Examples of this transformation: * a.b.c => a.b.c * a.b.c.prototype => a.b.c */ private Node stripPrototype(Node qualifiedName) { if (qualifiedName.getType() == Token.GETPROP && qualifiedName.getLastChild().getString().equals("prototype")) { return qualifiedName.getFirstChild(); } return qualifiedName; } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.getType() == Token.GETPROP) { String qualifiedName = callee

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>.getQualifiedName(); if ((functionName).equals(qualifiedName)) { className = callee.getNext().getString(); } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.getType() == Token.ARRAYLIT) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.getType() == Token.STRING) { typeNames.add(name.getString()); } } return typeNames; } } return null; } @Override public String identifyTypeDefAssign(Node n) { Node firstChild = n.getFirstChild(); int type = n.getType(); if (type == Token.ASSIGN) { if (TYPEDEF_NAME.equals(n.getLastChild().getQualifiedName())) { return firstChild.getQualifiedName(); } } else if (type == Token.VAR && firstChild.hasChildren()) { if (TYPEDEF_NAME.equals( firstChild.getFirstChild().getQualifiedName())) { return firstChild.getString(); } } return null; } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName) || "goog$add

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>SingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return null; } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { functionType.defineDeclaredProperty("getInstance", getterType, false); functionType.defineDeclaredProperty("instance_", objectType, false); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.getType() == Token.CALL); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()); } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { Preconditions.checkArgument(callNode.getType() == Token.CALL); Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callName.getChildCount() != 2) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (objectNode.getType() != Token.OBJECTLIT) { t.getCompiler().report(JSError.make(t.getSourceName(), callNode, OBJECTLIT_EXPECTED)); return null; } return new ObjectLiteralCast(typeNode.getQualifiedName(), typeNode.getNext()); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return ImmutableList.<AssertionFunctionSpec>

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> "is_dispatcher"; case DIRECTIVES: return "directives"; case DIRECT_EVAL: return "direct_eval"; default: Kit.codeBug(); } return null; } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof NumberNode && getDouble() == ((NumberNode) node).getDouble()); } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override public boolean isEquivalentTo(Node node) { return (node instanceof StringNode && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key. The * QUOTED_PROP int property is only

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> left, mid, mid2, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children, int lineno, int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>(propType); if (item == null) { return null; } return item.objectValue; } public boolean getBooleanProp(int propType) { return getIntProp(propType, 0) != 0; } public int getIntProp(int propType, int defaultValue) { PropListItem item = lookupProperty(propType); if (item == null) { return defaultValue; } return item.intValue; } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { Kit.codeBug(); } return item.intValue; } public void putProp(int propType, Object prop) { if (prop == null) { removeProp(propType); } else { PropListItem item = ensureProperty(propType); item.objectValue = prop; } } public void putBooleanProp(int propType, boolean prop) { putIntProp(propType, prop ? 1 : 0); } public void putIntProp(int propType, int prop) { PropListItem item = ensureProperty(propType); item.intValue = prop; } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.next) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.next) { count--; keys[count] = x.type; } Arrays.sort(keys); return keys; } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node");

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } return String.valueOf(type); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first.getType() == Token.STRING) { sb.append(first.getString()); } } else if (this instanceof ScriptOrFnNode) { ScriptOrFnNode sof = (ScriptOrFnNode) this; if (this instanceof FunctionNode)

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> { FunctionNode fn = (FunctionNode) this; sb.append(' '); sb.append(fn.getFunctionName()); } if (printSource) { sb.append(" [source name: "); sb.append(sof.getSourceName()); sb.append("] [encoded source length: "); sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart()); sb.append("] [base line: "); sb.append(sof.getBaseLineno()); sb.append("] [end line: "); sb.append(sof.getEndLineno()); sb.append(']'); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { case TARGETBLOCK_PROP: // can't add this as it recurses value = "target block property"; break; case LOCAL_BLOCK_PROP: // can't add this as it is dull value = "last local block"; break; case ISNUMBER_PROP: switch (x.intValue) { case BOTH: value = "both"; break; case RIGHT: value = "right"; break; case LEFT: value = "left"; break; default: throw Kit.codeBug(); } break; case SPECIALCALL_PROP: switch (x.intValue) { case SPECIALCALL_EVAL: value = "eval"; break; case SPECIALCALL_WITH: value = "with"; break; default: // NON_SPECIALCALL should not be stored throw Kit.codeBug(); } break; default: Object obj = x.objectValue; if (obj

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> != null) { value = obj.toString(); } else { value = String.valueOf(x.intValue); } break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append(" : "); sb.append(jsTypeString); } } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuffer s = new StringBuffer(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Appendable appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { if (Token.printTrees) { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getNodeClass(this) == getNodeClass(node2)) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeEqualsImpl(n2); if (res != null) { return res; } } return res; } /** * Checks if the subtree under this node is the same as another subtree * including types. Returns null if it's equal, or a message describing the * differences. */ public boolean checkTreeTypeAwareEqualsSilent(Node node2) { return checkTreeTypeAwareEqualsImpl(node2) == null; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getClass() == node2.getClass() && JSType.isEquivalent(jsType, node2.getJSType())) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.check

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>TreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.EOF: return "eof"; case Token.EOL: return "eol"; case Token.ENTERWITH: return "enterwith"; case Token.LEAVEWITH: return "leavewith"; case Token.RETURN: return "return"; case Token.GOTO: return "goto"; case Token.IFEQ: return "ifeq"; case Token.IFNE: return "ifne"; case Token.SETNAME: return "setname"; case Token.BITOR: return "bitor"; case Token.BITXOR: return "bitxor"; case Token.BITAND: return "bitand"; case Token.EQ: return "eq"; case Token.NE: return "ne"; case Token.LT: return "lt"; case Token.LE: return "le"; case Token.GT: return "gt"; case Token.GE: return "ge"; case Token.LSH: return "lsh"; case Token.RSH: return "rsh"; case Token.URSH: return "ursh"; case Token.ADD: return "add"; case Token.SUB: return "sub"; case Token.MUL: return "mul"; case Token.DIV: return "div"; case Token.MOD: return "mod"; case Token.BITNOT: return "bitnot"; case Token.NEG: return "neg"; case Token.NEW: return "new"; case Token.DELPROP: return "delprop"; case Token.TYPEOF: return "typeof"; case Token.GETPROP: return "getprop"; case Token.SETPROP: return "setprop"; case Token.GETELEM: return "getelem"; case Token.SETELEM: return "setelem"; case Token.CALL: return "call"; case Token.NAME: return "name"; case Token.NUMBER

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>: return "number"; case Token.STRING: return "string"; case Token.NULL: return "null"; case Token.THIS: return "this"; case Token.FALSE: return "false"; case Token.TRUE: return "true"; case Token.SHEQ: return "sheq"; case Token.SHNE: return "shne"; case Token.REGEXP: return "regexp"; case Token.POS: return "pos"; case Token.BINDNAME: return "bindname"; case Token.THROW: return "throw"; case Token.IN: return "in"; case Token.INSTANCEOF: return "instanceof"; case Token.GETVAR: return "getvar"; case Token.SETVAR: return "setvar"; case Token.TRY: return "try"; case Token.TYPEOFNAME: return "typeofname"; case Token.THISFN: return "thisfn"; case Token.SEMI: return "semi"; case Token.LB: return "lb"; case Token.RB: return "rb"; case Token.LC: return "lc"; case Token.RC: return "rc"; case Token.LP: return "lp"; case Token.RP: return "rp"; case Token.COMMA: return "comma"; case Token.ASSIGN: return "assign"; case Token.ASSIGN_BITOR: return "assign_bitor"; case Token.ASSIGN_BITXOR: return "assign_bitxor"; case Token.ASSIGN_BITAND: return "assign_bitand"; case Token.ASSIGN_LSH: return "assign_lsh"; case Token.ASSIGN_RSH: return "assign_rsh"; case Token.ASSIGN_URSH: return "assign_ursh"; case Token.ASSIGN_ADD: return "assign_add"; case Token.ASSIGN_SUB: return "assign_sub"; case Token.ASSIGN_MUL: return "assign_mul"; case Token.ASSIGN_DIV: return "assign_div"; case Token.ASSIGN_MOD: return "assign_mod"; case Token.HOOK: return "hook

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>"; case Token.COLON: return "colon"; case Token.OR: return "or"; case Token.AND: return "and"; case Token.INC: return "inc"; case Token.DEC: return "dec"; case Token.DOT: return "dot"; case Token.FUNCTION: return "function"; case Token.EXPORT: return "export"; case Token.IMPORT: return "import"; case Token.IF: return "if"; case Token.ELSE: return "else"; case Token.SWITCH: return "switch"; case Token.CASE: return "case"; case Token.DEFAULT: return "default"; case Token.WHILE: return "while"; case Token.DO: return "do"; case Token.FOR: return "for"; case Token.BREAK: return "break"; case Token.CONTINUE: return "continue"; case Token.VAR: return "var"; case Token.WITH: return "with"; case Token.CATCH: return "catch"; case Token.FINALLY: return "finally"; case Token.RESERVED: return "reserved"; case Token.NOT: return "not"; case Token.VOID: return "void"; case Token.BLOCK: return "block"; case Token.ARRAYLIT: return "arraylit"; case Token.OBJECTLIT: return "objectlit"; case Token.LABEL: return "label"; case Token.TARGET: return "target"; case Token.LOOP: return "loop"; case Token.EXPR_VOID: return "expr_void"; case Token.EXPR_RESULT: return "expr_result"; case Token.JSR: return "jsr"; case Token.SCRIPT: return "script"; case Token.EMPTY: return "empty"; case Token.GET_REF: return "get_ref"; case Token.REF_SPECIAL: return "ref_special"; } return "<unknown="+token+">"; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { if (type == Token.ARRAYLIT) { try { int

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>[] indices1 = (int[]) getProp(Node.SKIP_INDEXES_PROP); int[] indices2 = (int[]) node.getProp(Node.SKIP_INDEXES_PROP); if (indices1 == null) { if (indices2 != null) { return false; } } else if (indices2 == null) { return false; } else if (indices1.length != indices2.length) { return false; } else { for (int i = 0; i < indices1.length; i++) { if (indices1[i] != indices2[i]) { return false; } } } } catch (Exception e) { return false; } } else if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDECR_PROP, 0); int post2 = node.getIntProp(INCRDECR_PROP, 0); if (post1 != post2) { return false; } } else if (type == Token.STRING) { int quoted1 = this.getIntProp(QUOTED_PROP, 0); int quoted2 = node.getIntProp(QUOTED_PROP, 0); if (quoted1 != quoted2) { return false; } } return true; } public boolean hasSideEffects() { switch (type) { case Token.EXPR_VOID: case Token.COMMA: if (last != null) return last.hasSideEffects(); else return true; case Token.HOOK: if (first == null || first.next == null || first.next.next == null) { Kit.codeBug(); } return first.next.hasSideEffects() && first.next.next.hasSideEffects(); case Token.ERROR: // Avoid cascaded error messages case Token.EXPR_RESULT: case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND:

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ENTERWITH: case Token.LEAVEWITH: case Token.RETURN: case Token.GOTO: case Token.IFEQ: case Token.IFNE: case Token.NEW: case Token.DELPROP: case Token.SETNAME: case Token.SETPROP: case Token.SETELEM: case Token.CALL: case Token.THROW: case Token.RETHROW: case Token.SETVAR: case Token.CATCH_SCOPE: case Token.RETURN_RESULT: case Token.SET_REF: case Token.DEL_REF: case Token.REF_CALL: case Token.TRY: case Token.SEMI: case Token.INC: case Token.DEC: case Token.EXPORT: case Token.IMPORT: case Token.IF: case Token.ELSE: case Token.SWITCH: case Token.WHILE: case Token.DO: case Token.FOR: case Token.BREAK: case Token.CONTINUE: case Token.VAR: case Token.CONST: case Token.WITH: case Token.CATCH: case Token.FINALLY: case Token.BLOCK: case Token.LABEL: case Token.TARGET: case Token.LOOP: case Token.JSR: case Token.SETPROP_OP: case Token.SETELEM_OP: case Token.LOCAL_BLOCK: case Token.SET_REF_OP: return true; default: return false; } } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>") public Set<String> getDirectives() { return (Set<String>) getProp(DIRECTIVES); } /** * Sets the warning suppressions on this node. */ public void setSuppressions(Set<String> val) { Preconditions.checkState(type == Token.FUNCTION || type == Token.SCRIPT); putProp(SUPPRESSIONS, val); } /** * Returns the set of supressions for this node. */ @SuppressWarnings("unchecked") public Set<String> getSuppressions() { return (Set<String>) getProp(SUPPRESSIONS); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setWasEmptyNode(boolean val) { putBooleanProp(EMPTY_BLOCK, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean wasEmptyNode() { return getBooleanProp(EMPTY_BLOCK); } /** * Marks this function or constructor call node as having no side effects. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setIsNoSideEffectsCall() { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putBooleanProp(NO_SIDE_EFFECTS_CALL, true); } /** * Returns true if this node is a function or constructor call that * has no side effects. */ public boolean isNoSideEffectsCall() { return getBooleanProp(NO_SIDE_EFFECTS_CALL); } /** * This should only be called for STRING nodes created in object lits. */ public boolean isQuotedString() { return false; } /** * This should only be called for STRING nodes created in object lits. */ public void setQuotedString() { Kit.codeBug(); } static class NodeMismatch { final Node nodeA; final Node nodeB; NodeMismatch(Node nodeA, Node nodeB) { this.nodeA = node

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> } /** * Records declared type names. Given the limited scopes of JavaScript, all * named types are dumped in a common global scope. We may need to revise this * assumption in the future. * * @param name The name of the type to be recorded. * @param t The actual type being associated with the name. * @return True if this name is not already defined, false otherwise. */ public boolean declareType(String name, JSType t) { if (namesToTypes.containsKey(name)) { return false; } register(t, name); return true; } /** * Records a forward-declared type name. We will not emit errors if this * type name never resolves to anything. */ public void forwardDeclareType(String name) { forwardDeclaredTypes.add(name); } /** * Whether this is a forward-declared type name. */ public boolean isForwardDeclaredType(String name) { return forwardDeclaredTypes.contains(name); } /** Determines whether the given JS package exists. */ public boolean hasNamespace(String name) { return namespaces.contains(name); } /** * Looks up a type by name. * * @param jsTypeName The name string. * @return the corresponding JSType object or {@code null} it cannot be found */ public JSType getType(String jsTypeName) { // TODO(user): Push every local type name out of namesToTypes so that // NamedType#resolve is correct. if (jsTypeName.equals(templateTypeName)) { return templateType; } return namesToTypes.get(jsTypeName); } public JSType getNativeType(JSTypeNative typeId) { return nativeTypes[typeId.ordinal()]; } public ObjectType getNativeObjectType(JSTypeNative typeId) { return (ObjectType) getNativeType(typeId); } public FunctionType getNativeFunctionType(JSTypeNative typeId) { return (FunctionType) getNativeType(typeId); } /** * Looks up a type by name. To allow for forward references to types, an * unrecognized string has to be bound to a NamedType object that will be * resolved later. * * @param scope A scope for

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> doing type name resolution. * @param jsTypeName The name string. * @param sourceName The name of the source file where this reference appears. * @param lineno The line number of the reference. * @return a NamedType if the string argument is not one of the known types, * otherwise the corresponding JSType object. */ public JSType getType(StaticScope<JSType> scope, String jsTypeName, String sourceName, int lineno, int charno) { JSType type = getType(jsTypeName); if (type == null) { // TODO(user): Each instance should support named type creation using // interning. NamedType namedType = new NamedType(this, jsTypeName, sourceName, lineno, charno); unresolvedNamedTypes.put(scope, namedType); type = namedType; } return type; } /** * Resolve all the unresolved types in the given scope. */ public void resolveTypesInScope(StaticScope<JSType> scope) { for (NamedType type : unresolvedNamedTypes.get(scope)) { type.resolve(reporter, scope); } resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope)); if (scope != null && scope.getParentScope() == null) { // By default, the global "this" type is just an anonymous object. // If the user has defined a Window type, make the Window the // implicit prototype of "this". PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType( JSTypeNative.GLOBAL_THIS); JSType windowType = getType("Window"); if (globalThis.isUnknownType()) { ObjectType windowObjType = ObjectType.cast(windowType); if (windowObjType != null) { globalThis.setImplicitPrototype(windowObjType); } else { globalThis.setImplicitPrototype( getNativeObjectType(JSTypeNative.OBJECT_TYPE)); } } } } /** * Creates a type representing optional values of the given type. * @return the union of the type and the void type */ public JSType createOptionalType(JSType type) { if (type instanceof UnknownType || type.isAllType()) { return type; } else { return

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of an enum before we actually declare it. */ public void identifyEnumName(String name) { enumTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { return createFromTypeNodes(n, sourceName, scope, false); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. * @param forgiving Whether we should be forgiving about type names * that we can't find. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope, boolean forgiving) { if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName, forgiving); } } return createFromTypeNodesInternal(n, sourceName, scope, forgiving); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope, boolean) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, Static

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>Scope<JSType> scope, boolean forgiving) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope, forgiving) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope, forgiving)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope, false)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope, false)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope, false)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (forgiving) { namedType.forgiveUnknownNames(); } if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> ObjectType) && !(enumTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope, false); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope, false); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; Node current = n.getFirstChild(); if (current.getType() == Token.THIS) { Node thisNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( thisNode, sourceName, scope, false) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.thisnotobject"), sourceName, thisNode.getLineno(), "", thisNode.getCharno()); } current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.LP) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope, false)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope, false); if (arg.getType() == Token.EQUALS) { boolean

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), "", arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope, false); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope, false); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSType

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Roger Lawrence * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; public class FunctionNode extends ScriptOrFnNode { private static final long serialVersionUID = 1L; public FunctionNode(String name) { super(Token.FUNCTION); functionName = name; } public FunctionNode(String name, int lineno, int charno) { super(Token.FUNCTION, lineno, charno); functionName = name; } public String getFunctionName() { return

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS> arrowType, ObjectType typeOfThis, String templateTypeName, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkNotNull(arrowType); this.source = source; this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY; if (isConstructor) { this.typeOfThis = typeOfThis != null && typeOfThis.isNoObjectType() ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } this.call = arrowType; this.templateTypeName = templateTypeName; } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.LP), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name, source); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. return isEquivalentTo(registry.getNativeType(U2U_CONSTRUCTOR_TYPE)); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } @Override public boolean isFunctionType()

Closure, 98

<FILEB>
<CHANGES>
for (BasicBlock block = ref.getBasicBlock();
block!= null; block = block.getParent()) {
if (block.isFunction) {
break;
} else if (block.isLoop) {
return false;
}
}
<CHANGEE>
<CHANGES>
private final boolean isFunction;
<CHANGEE>
<CHANGES>
private final boolean isLoop;
<CHANGEE>
<CHANGES>
this.isFunction = root.getType() == Token.FUNCTION;
if (root.getParent()!= null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
<CHANGEE>
<FILEE>
<FILEB> int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. <CHANGES> <CHANGEE> return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { public String getSourceName() { return sourceName; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ <CHANGES> <CHANGEE> /** * Whether this block denotes a loop. */ <CHANGES> <CHANGEE> /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); <CHANGES> <CHANGEE> } BasicBlock getParent() { return parent; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If that<SCANS>ObjectType type : implementedInterfaces) { registry.registerTypeImplementingInterface(this, type); } this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); } @Override public boolean hasProperty(String name) { return super.hasProperty(name) || "prototype".equals(name); } @Override public boolean hasOwnProperty(String name) { return super.hasOwnProperty(name) || "prototype".equals(name); } @Override public JSType getPropertyType(String name) { if ("prototype".equals(name)) { return getPrototype(); } else { if (!hasOwnProperty(name)) { if ("call".equals(name)) { // Define the "call" function lazily. Node params = getParametersNode(); if (params == null) { // If there's no params array, don't do any type-checking // in this CALL function. defineDeclaredProperty(name, new FunctionBuilder(registry) .withReturnType(getReturnType()) .build(), false); } else { params = params.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); thisTypeNode.setOptionalArg(true); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParamsNode(params) .withReturnType(getReturnType()) .build(), false); } } else if ("apply".equals(name)) { // Define the "apply" function lazily. FunctionParamBuilder builder = new FunctionParamBuilder(registry); // Ecma-262 says that apply's second argument must be an Array // or an arguments object. We don't model the arguments object, // so let's just be forgiving for now. // TODO(nicksantos): Model the Arguments object. builder.addOptionalParams( registry.createNullableType(getTypeOfThis()), registry.createNullableType( registry.getNativeType(JSTypeNative.OBJECT_TYPE))); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParams(builder) .withReturnType(getReturnType